FOR_EACH 调用宏中有两个或更多参数的宏

FOR_EACH macro with two or more params in call macro

下面是一些模拟 for 循环以消除复制和粘贴代码的 C++ 宏代码。

#define SEMICOLON ;
#define LOL(x) print(x)
#define LOLZ(...) FOR_EACH(LOL, SEMICOLON, ##__VA_ARGS__)

LOLZ("hi", "my", "friend", "!");

// result
print("hi"); print("my"); print("friend"); print("!");

我还可以展示创建这个宏的代码(我找到了这个 here on Stack Overflow):

#define EXPAND(x) x
#define FOR_EACH_1(what, delimiter, x, ...) what(x)
#define FOR_EACH_2(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_1(what, delimiter, __VA_ARGS__))
#define FOR_EACH_3(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_2(what, delimiter, __VA_ARGS__))
#define FOR_EACH_4(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_3(what, delimiter, __VA_ARGS__))
#define FOR_EACH_5(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_4(what, delimiter, __VA_ARGS__))
#define FOR_EACH_6(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_5(what, delimiter, __VA_ARGS__))
#define FOR_EACH_7(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_6(what, delimiter, __VA_ARGS__))
#define FOR_EACH_8(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_7(what, delimiter, __VA_ARGS__))
#define FOR_EACH_9(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_8(what, delimiter, __VA_ARGS__))
#define FOR_EACH_10(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_9(what, delimiter, __VA_ARGS__))
#define FOR_EACH_11(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_10(what, delimiter, __VA_ARGS__))
#define FOR_EACH_12(what, delimiter, x, ...)\
  what(x) delimiter\
  EXPAND(FOR_EACH_11(what, delimiter, __VA_ARGS__))
#define FOR_EACH_13(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_12(what, delimiter, __VA_ARGS__))
#define FOR_EACH_14(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_13(what, delimiter, __VA_ARGS__))
#define FOR_EACH_15(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_14(what, delimiter, __VA_ARGS__))
#define FOR_EACH_16(what, delimiter, x, ...)\
  what(x) delimiter \
  EXPAND(FOR_EACH_15(what, delimiter, __VA_ARGS__))

#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
#define FOR_EACH_NARG_(...) EXPAND(FOR_EACH_ARG_N(__VA_ARGS__))
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, N, ...) N
#define FOR_EACH_RSEQ_N() 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#define CONCATENATE(x,y) x##y
#define FOR_EACH_(N, what, delimiter, ...) EXPAND(CONCATENATE(FOR_EACH_, N)(what, delimiter, __VA_ARGS__))


#define FOR_EACH(what, delimiter, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, delimiter, __VA_ARGS__)

但是我对函数的两个参数有困难: 我需要在此宏的一次迭代中使用 func(x, y) 。 调用宏必须如下所示:

MY_DUAL_FOREACH_LIKE_MACRO(
  x1, y1,
  x2, y2,
  x3, y3
)

// and I expect to get:
func(x1, y1); func(x2, y2); func(x3, y3); 

如果我在宏的 "x" 参数附近添加 "y" 我将得到调用 "what" 参数的 x2 unsed 替换:

func(x1, y1); func(x2, y2); func(x3, y3); func(, ); func(, ); func(, ); 

如果您有这方面的经验,请帮助我将此宏重新编码为双参数类型 FOR_EACH 宏。

如果您用括号将宏参数分组,则每组将作为一个参数处理。您可能会修改宏,以便可以像这样调用它:

MY_DUAL_FOREACH_LIKE_MACRO(
  (x1, y1),
  (x2, y2),
  (x3, y3)
)

然后让宏将 func(x1, y1) 粘贴在一起。

通常,我赞成按说明回答问题,假设选择该解决方案是有原因的。但是我对 21 世纪在 C++ 中使用宏有相当强烈的看法,所以我还是将其作为答案发布。


拜托,不要这样滥用宏。

如果您想调用带有参数对的函数,这里有一个完全可以接受的、可读的、可调试的、可维护的、没有宏的解决方案:

std::vector<std::pair<double, double>> values = {
    { x1, y1 },
    { x2, y2 },
    { x3, y3 }
};

for(auto& pair : values)
{
    f(pair.first, pair.second);
}

如果愿意,您也可以将它们放在一个向量中:

std::vector<double> values = {
    x1, y1,
    x2, y2,
    x3, y3
};

for(int i = 0; i < values.size(); i += 2)
{
    f(values[i], values[i + 1]);
}

根据我更好的判断,我将回答这个问题(因为它在少数情况下很有用)。我修改了您发布的代码,因此它需要两个参数 (demo):

#include <iostream>

#define EXPAND(x) x
#define FOR_EACH_2(what, delimiter, x, y) what((x), (y))
#define FOR_EACH_4(what, delimiter, x, y, ...)\
  what((x), (y)) delimiter \
  EXPAND(FOR_EACH_2(what, delimiter, __VA_ARGS__))
#define FOR_EACH_6(what, delimiter, x, y, ...)\
  what((x), (y)) delimiter \
  EXPAND(FOR_EACH_4(what, delimiter, __VA_ARGS__))

#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
#define FOR_EACH_NARG_(...) EXPAND(FOR_EACH_ARG_N(__VA_ARGS__))
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N
#define FOR_EACH_RSEQ_N() 6, 5, 4, 3, 2, 1, 0
#define CONCATENATE(x,y) x##y
#define FOR_EACH_(N, what, delimiter, ...) EXPAND(CONCATENATE(FOR_EACH_, N)(what, delimiter, __VA_ARGS__))

#define FOR_EACH(what, delimiter, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, delimiter, __VA_ARGS__)

void foo(int x, float y) {
    std::cout << "foo(" << x << ", " << y << ")\n";
}

int main() {
    FOR_EACH(foo, ;, 1, 3.14, 2, 1.41, 3, 1.73);
}

输出为:

foo(1, 3.14)
foo(2, 1.41)
foo(3, 1.73)

如果你想调用 foo 超过 3 次,你必须添加额外的案例,我将把它作为练习留给 reader。

这可以被清理,并且可能应该被赋予一个与 FOR_EACH 不同的名称(也许类似于 FOR_EACH_2 表明它一次对 2 个参数进行操作),我将作为另一个练习留给 reader.

我将事情作为练习留给 reader 的原因是不鼓励使用这个答案(但不是完全阻止它)。如果您懒得清理它并根据您的需要对其进行定制,那就由您来做。