如何使用分隔符拼接C++可变参数宏__VA_ARGS__?
问题描述
希望实现一个可变参数宏 FOO(...),将传入的可变参数用下划线_拼接后,作为单个参数传给另一个宏BAR。例如:
FOO(a, b, c, d);
需要展开为:
BAR(a_b_c_d);
要求优先使用C++17及更早特性,不依赖Boost等第三方库,已知可通过__VA_ARGS__处理可变参数、##进行拼接,询问具体实现方式。
解决方案
C++17及更早的预处理器没原生支持任意长度可变参数的自动拼接,得靠预处理器递归展开和##拼接操作符来实现。核心思路是定义一系列宏,把可变参数逐个拆解、拼接成下划线连接的单个参数,再传给BAR。
具体代码实现(以最多支持4个参数为例)
// 辅助宏:负责两两拼接参数,中间加下划线 #define CONCAT_2(a, b) a##_##b // 三个参数:先拼前两个,再和第三个拼接 #define CONCAT_3(a, b, c) CONCAT_2(CONCAT_2(a, b), c) // 四个参数:层层嵌套完成拼接 #define CONCAT_4(a, b, c, d) CONCAT_3(CONCAT_2(a, b), c, d) // 辅助宏:根据传入参数的个数,自动匹配对应的拼接宏 #define SELECT_CONCAT(_1, _2, _3, _4, NAME, ...) NAME // 把可变参数传入,让SELECT_CONCAT挑选合适的CONCAT_N宏执行 #define CONCAT(...) SELECT_CONCAT(__VA_ARGS__, CONCAT_4, CONCAT_3, CONCAT_2, )(__VA_ARGS__) // 目标宏FOO:先完成参数拼接,再传给BAR #define FOO(...) BAR(CONCAT(__VA_ARGS__)) // 测试用的BAR宏(实际使用时替换为你自己的宏即可) #define BAR(X) X
测试示例:
FOO(a, b, c, d); // 展开结果:BAR(a_b_c_d) FOO(x, y); // 展开结果:BAR(x_y)
补全单个参数的处理逻辑
上面的代码处理单个参数会报错,因为没有对应的拼接宏,需要补充:
// 单个参数直接返回自身 #define CONCAT_1(a) a // 更新SELECT_CONCAT,加入单个参数的拼接宏 #define SELECT_CONCAT(_1, _2, _3, _4, NAME, ...) NAME #define CONCAT(...) SELECT_CONCAT(__VA_ARGS__, CONCAT_4, CONCAT_3, CONCAT_2, CONCAT_1)(__VA_ARGS__)
现在单个参数也能正常处理:
FOO(z); // 展开结果:BAR(z)
扩展支持更多参数
如果需要处理5个及以上参数,只需继续添加对应的CONCAT_N宏,并更新SELECT_CONCAT的参数列表即可。比如支持5个参数的版本:
#define CONCAT_5(a,b,c,d,e) CONCAT_4(CONCAT_2(a,b),c,d,e) #define SELECT_CONCAT(_1, _2, _3, _4, _5, NAME, ...) NAME #define CONCAT(...) SELECT_CONCAT(__VA_ARGS__, CONCAT_5, CONCAT_4, CONCAT_3, CONCAT_2, CONCAT_1)(__VA_ARGS__)
原理简述
SELECT_CONCAT利用预处理器的参数匹配特性:传入N个参数时,__VA_ARGS__会占据前N个位置,最终匹配到对应的CONCAT_N宏。CONCAT_N通过嵌套调用CONCAT_2,逐步将所有参数用下划线连接成单个标识符。FOO宏最终将拼接完成的结果作为参数传递给BAR。
内容的提问来源于stack exchange,提问作者Wowbagger and his liquid lunch
相关产品推荐
相关产品推荐

