#define PP_ARG0_(arg0, ...) arg0
#define PP_REST_(arg0, ...) __VA_ARGS__
#define PP_ARG0(args) PP_ARG0_ args
#define PP_REST(args) PP_REST_ args
#define FUNCTION(name) void name();
#define FUNCTION_TABLE(...) \
FUNCTION(PP_ARG0((__VA_ARGS__))) \
FUNCTION_TABLE(PP_REST((__VA_ARGS__))) \
测试代码:
FUNCTION_TABLE(f1, f2,f3,testA,testB,testC);
显然,由于递归扩展,它只会声明void f1();
而其余部分不会扩展:
void f1(); FUNCTION_TABLE(f2,f3,testA,testB,testC);
在这种情况下,我可以使用什么样的技巧来实现递归扩展?问题是我需要支持许多参数(最多100个),我绝对不能使用boost。
答案 0 :(得分:9)
最简单的解决方案是使用这样的序列迭代:
#define CAT(x, y) PRIMITIVE_CAT(x, y)
#define PRIMITIVE_CAT(x, y) x ## y
#define FUNCTION(name) void name();
#define FUNCTION_TABLE(seq) CAT(FUNCTION_TABLE_1 seq, _END)
#define FUNCTION_TABLE_1(x) FUNCTION(x) FUNCTION_TABLE_2
#define FUNCTION_TABLE_2(x) FUNCTION(x) FUNCTION_TABLE_1
#define FUNCTION_TABLE_1_END
#define FUNCTION_TABLE_2_END
然后使用预处理程序序列而不是varidiac参数调用FUNCTION_TABLE
:
FUNCTION_TABLE((f1)(f2)(f3)(testA)(testB)(testC))
这不仅比使用递归解决方案更简单,也更快(即编译速度更快)(就像你展示的那个或者喜欢这个here)。
答案 1 :(得分:4)
以下是有人想要做同样的答案。
#define _PP_0(_1, ...) _1 // (a,b,c,d) => a
#define _PP_X(_1, ...) (__VA_ARGS__) // (a,b,c,d) => (b,c,d)
//for each a in __VA_ARGS__ do f(a,x)
//where x is some parameter passed to PP_TRANSFORM
#define PP_TRANSFORM(f,x,...) \
PP_JOIN(PP_TRANSFORM_,PP_NARG(__VA_ARGS__))(f,x,(__VA_ARGS__))
#define PP_TRANSFORM_0(...)
#define PP_TRANSFORM_1( f,x,a) f(_PP_0 a,x) PP_TRANSFORM_0( f,x,_PP_X a)
#define PP_TRANSFORM_2( f,x,a) f(_PP_0 a,x) PP_TRANSFORM_1( f,x,_PP_X a)
...
#define PP_TRANSFORM_51(f,x,a) f(_PP_0 a,x) PP_TRANSFORM_50( f,x,_PP_X a)
...
#define PP_TRANSFORM_99(f,x,a) f(_PP_0 a,x) PP_TRANSFORM_98(f,x,_PP_X a)
#define PP_TRANSFORM_100(f,x,a)f(_PP_0 a,x) PP_TRANSFORM_99(f,x,_PP_X a)
其中PP_NARG
是计算参数数量的宏,PP_JOIN
是加入令牌的宏(即PP_JOIN(a,b) => ab
)。如果您希望能够处理超过64个参数,则还需要修补PP_NARG
。
现在,回到最初的问题。使用PP_TRANSFORM
的解决方案是:
#define FUNCTION(name, dummy) void name();
#define FUNCTION_TABLE(...) PP_TRANSFORM(FUNCTION,dummy,__VA_ARGS__)
如果要生成c ++实现函数,则可以使用PP_TRANSFORM
的opaque x参数:
#define FUNCTION_CPP(name, class) void class::name(){}
#define FUNCTION_TABLE_CPP(...) PP_TRANSFORM(FUNCTION_CPP,MyClass,__VA_ARGS__)
所有这些对GCC和MSVC预处理器同样有效; PP_TRANSFORM_NN不使用__VA_ARGS__
来避免单独实现GCC和MSVC的100个定义
答案 2 :(得分:0)
我真的很难理解这个序列迭代概念,但是将 above answer 和答案 here 拼凑在一起 - 并且,一点一点地工作...... - 我相信我已经想通了以及如何解释和理解它。
这是一些友好的代码,我已经对其进行了调整:
__VA_OPT__
(如果不需要该部分可以删除)注意参数必须以(() () ())
而不是( , , )
格式指定,因为函数风格的宏依赖它来处理任何参数. 请参阅下面的代码以获得深入的解释性演练。
#include <iostream>
#define CONCAT(a, ...) a ## __VA_ARGS__
#define CONCAT_FUNC(a, ...) CONCAT(a, __VA_ARGS__)
//Whatever is inside each () will be prepended and appended with what's here.
#define MyVariadicMacro(...) << __VA_ARGS__ + 7 << " "
#define MyVariadicMacro2(...) << __VA_ARGS__ << " "
#define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_B
#define RESOLVE_B(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_A
#define RESOLVE_A_END
#define RESOLVE_B_END
#define RECURSE(...) CONCAT_FUNC(RESOLVE_A __VA_ARGS__, _END)
int main()
{
//Choose your own variadic macro, to provide what to prepend and append to each variadic argument!
#define VARIADIC_FUNC MyVariadicMacro
//Note: Empty ()'s are in here just to provide an example that they can be ignored via. __VA_OPT__().
std::cout RECURSE(() (0) () (1) (2) (3) ());
//Swapping out with variadic macro is being utilized for RECURSE.
#undef VARIADIC_FUNC
#define VARIADIC_FUNC MyVariadicMacro2
std::cout RECURSE(() (0) () (1) (2) (3) ());
#undef VARIADIC_FUNC
return 0;
}
Output: 7 8 9 10 0 1 2 3
//Starting with:
std::cout RECURSE(() (0) () (1) (2) (3) ());
//Apply: #define RECURSE(...) CONCAT_FUNC(RESOLVE_A __VA_ARGS__, _END)
std::cout CONCAT_FUNC(RESOLVE_A() (0) () (1) (2) (3) (), _END);
//Apply: #define CONCAT_FUNC(a, ...) CONCAT(a, __VA_ARGS__)
std::cout CONCAT(RESOLVE_A() (0) () (1) (2) (3) (), _END);
//Apply: #define CONCAT(a, ...) a ## __VA_ARGS__
std::cout RESOLVE_A() (0) () (1) (2) (3) () ## _END;
//Apply: #define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_B
//Note: Since the () is empty, the __VA_OPT__() part is simply skipped.
std::cout RESOLVE_B(0) () (1) (2) (3) () ## _END;
//Apply: #define RESOLVE_B(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_A
std::cout VARIADIC_FUNC(0) RESOLVE_A() (1) (2) (3) () ## _END;
//Apply: #define MyVariadicMacro(...) << __VA_ARGS__ + 7 << " "
//Apply: #define VARIADIC_FUNC MyVariadicMacro
std::cout << 0 + 7 << " " RESOLVE_A() (1) (2) (3) () ## _END;
//Apply: #define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_B
//Note: Since the () is empty, the __VA_OPT__() part is simply skipped.
std::cout << 0 + 7 << " " RESOLVE_B(1) (2) (3) () ## _END;
//And so on... ending up with:
//Note: Ending with empty () or non-empty() doesn't matter; either way, we will end up with a RESOLVE_?_END.
std::cout << 0 + 7 << " " << 1 + 7 << " " << 2 + 7 << " " << 3 + 7 << " " RESOLVE_A() ## _END;
//Apply: #define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__)) RESOLVE_B
//Note: Since the () is empty, the __VA_OPT__() part is simply skipped.
std::cout << 0 + 7 << " " << 1 + 7 << " " << 2 + 7 << " " << 3 + 7 << " " RESOLVE_B ## _END;
//Apply: ## simply concatenates.
std::cout << 0 + 7 << " " << 1 + 7 << " " << 2 + 7 << " " << 3 + 7 << " " RESOLVE_B_END;
//Apply: #define RESOLVE_B_END
//Note: In this particular case, we happened to end up with RESOLVE_B_END; in other cases, we will end with
//RESOLVE_A_END.
std::cout << 0 + 7 << " " << 1 + 7 << " " << 2 + 7 << " " << 3 + 7 << " ";
//Etc.
std::cout << 7 << " " << 8 << " " << 9 << " " << 10 << " ";
注意:如果您想将 RECURSE
与 X 宏概念一起使用,您必须做一些额外的事情。 X-macro 的问题是你会像这样定义它以便在递归宏中使用:
#define MyThing (() (0) () (1) (2) (3) ())
当你像那样使用它时,要么通过。 RECURSE()
或任何其他宏,它包含在一些额外的括号中:
//It interprets this as RECURSE((() (0) () (1) (2) (3) ())), which is bad.
std::cout RECURSE(MyThing);
解决办法就是用这样的宏,让它自然解析去掉括号。以下是为此修改 RECURSE()
的示例:
#define ESCAPE_PAREN(...) __VA_ARGS__
//Old RECURSE():
#define RECURSE(...) CONCAT_FUNC(RESOLVE_A __VA_ARGS__, _END)
//New RECURSE():
#define RECURSE(...) CONCAT_FUNC(RESOLVE_A ESCAPE_PAREN __VA_ARGS__, _END)
//Alternatively, just use this instead of RECURSE() (seems to work better):
#define RECURSE_ESCAPE(...) ESCAPE_PAREN __VA_ARGS__
需要注意的是,使用 ESCAPE_PAREN
时不会将 __VA_ARGS__
包裹在 ()
中。
编辑:我尝试在实际项目中使用它们后更新了上述宏。我还添加了一些其他可能有用的相关宏(RECURSE_FIRST
(仅通过第一个条目),RECURSE_LATTER
(仅在第一个条目之后通过后一个条目),以及它们的 {{1 }} 版本,和 _ESCAPE
(通过第一个条目,对其应用 RECURSE_SPLIT
宏,仅通过后面的条目,在那些条目上应用 VARIADIC_FUNC_FIRST()
宏,通过第一个再次输入,在其上应用 VARIADIC_FUNC()
宏,并将所有这些连接在一起......)):
VARIADIC_FUNC_END()
当我尝试在我的宏中使用逗号和分号时,我发现这些麻烦/限制要少得多......它们当然像上面一样工作,你可以使用以下形式的 X 宏:< /p>
//#define ESCAPE_PAREN(...) __VA_ARGS__
#define RESOLVE_A(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__) RESOLVE_B)
#define RESOLVE_B(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__) RESOLVE_A)
#define RECURSE(...) RESOLVE_A __VA_ARGS__
//#define RECURSE_ESCAPE(...) RECURSE(ESCAPE_PAREN __VA_ARGS__)
#define RESOLVE_FIRST(...) __VA_OPT__(VARIADIC_FUNC(__VA_ARGS__) DISCARD_A)
#define RESOLVE_LATTER(...) RESOLVE_B
#define DISCARD_A(...) __VA_OPT__(DISCARD_B)
#define DISCARD_B(...) __VA_OPT__(DISCARD_A)
#define RECURSE_FIRST(...) RESOLVE_FIRST __VA_ARGS__ ()
#define RECURSE_LATTER(...) RESOLVE_LATTER __VA_ARGS__ ()
#define RESOLVE_SPLIT_FIRST(...) __VA_OPT__(VARIADIC_FUNC_FIRST(__VA_ARGS__) DISCARD_A)
#define RESOLVE_SPLIT_END(...) __VA_OPT__(VARIADIC_FUNC_END(__VA_ARGS__) DISCARD_A)
#define RECURSE_SPLIT(...) RESOLVE_SPLIT_FIRST __VA_ARGS__ () RESOLVE_LATTER __VA_ARGS__ () RESOLVE_SPLIT_END __VA_ARGS__ ()
另见: