将一个宏传递到另一个宏而不是直接传递内容时会出现不需要的额外"空"参数
Unwanted extra "empty" argument appears when passing in a macro to another macro instead of passing things directly
问题(简洁(
(完整的最小代码如下(不知何故,当我将一个简单的参数列表传递给我的宏时,一切都很好。
#pragma message STRINGIZE( ( DUMMY_WRAPPER (initial_argument, arg2, arg3)))
在编译上述内容的过程中,我看到了编译器/预处理器的预期输出:
#pragma message: ( initial_argument { {initial_argument, arg2} , {initial_argument, arg3} }; 2)
但是,当我尝试定义带有参数的宏并将其传递到DUMMY_WRAPPER
时,我得到一个"额外"的空参数。例:
#define ARGS initial_argument, arg2, arg3
#pragma message STRINGIZE( ( DUMMY_WRAPPER (ARGS) ))
编译器输出(与正确的输出进行比较(:
#pragma message: ( initial_argument { {initial_argument, arg2} , {initial_argument, arg3} , {initial_argument, } }; 3 )
我如何摆脱这个额外的争论?
完整代码(附说明(
Nota Bene:我承认这可能是非常糟糕的宏滥用,并且有更好的方法可以使用C++进行元编程,但我只是想让它快速工作。
我使用 GCC/G++ 进行编译。
这是工作代码+编译器输出,供您轻松在线测试/实验: https://godbolt.org/z/wGFbrK
#define COMMA() ,
// routines for stringizing macros, similar to BOOST_PP_STRINGIZE
#define STR1(x) #x
#define STRINGIZE(x) STR1(x)
// routine & subroutines for argument counting
#define NARG(...)
NARG_(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define ARG_N(_1,_2,_3,_4,_5, N, ... ) N
#define NARG_(...) ARG_N(__VA_ARGS__)
// routines for "looped" macro expansion, for processing lists of macro arguments
#define LOOP_1(M, C, D, x) M(C, x)
#define LOOP_2(M, C, D, x, ...) M(C, x) D()
LOOP_1(M, C, D, __VA_ARGS__)
#define LOOP_3(M, C, D, x, ...) M(C, x) D()
LOOP_2(M, C, D, __VA_ARGS__)
#define LOOP_4(M, C, D, x, ...) M(C, x) D()
LOOP_3(M, C, D, __VA_ARGS__)
#define LOOP_5(M, C, D, x, ...) M(C, x) D()
LOOP_4(M, C, D, __VA_ARGS__)
// routine for concatenating things, used here to expand loop routine names, i.e. LOOP_ + 3 => LOOP_3
#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a##__VA_ARGS__
// **** TOP-LEVEL ****
// (code using above routines to demonstrate the problem)
// lists the first argument, i.e. C in the loop, and some other argument
#define LIST_FIELD(arg1, a, ... ) {arg1, a}
#define DUMMY_WRAPPER(arg1, ...) DUMMY( arg1, __VA_ARGS__)
#define DUMMY( arg1, ...)
DUMMY_2(arg1, NARG(__VA_ARGS__), __VA_ARGS__)
#define DUMMY_2( arg1, field_count, ...)
DUMMY_3(arg1, CAT(LOOP_, field_count), __VA_ARGS__)
field_count
#define DUMMY_3( arg1, loop, ...)
arg1 {
loop(LIST_FIELD, arg1, COMMA, __VA_ARGS__)
};
#pragma message STRINGIZE( ( DUMMY_WRAPPER (initial_argument, arg2, arg3)))
#define ARGS initial_argument, arg2, arg3
#pragma message STRINGIZE( ( DUMMY_WRAPPER (ARGS) ))
(一些(可能相关的研究(不确定(
https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
不确定这是否可以解释这里的事情,不过,直觉上我认为不是......
当你的编译器看到这个时:DUMMY_WRAPPER (ARGS)
它将使用ARGS
作为DUMMY_WRAPPER
的第一个参数,即使ARGS
的扩展包含逗号。
这可以通过从DUMMY_WRAPPER
中删除第一个参数来解决,并且仅使用__VA_ARGS__
:
#define DUMMY_WRAPPER(...) DUMMY(__VA_ARGS__)
或者通过将DUMMY_WRAPPER
包装在另一个宏中:
#define DUMMY_WRAPPER_2(...) DUMMY_WRAPPER(__VA_ARGS__)
事实上,您的代码根本不应该编译,因为...
宏参数必须至少接收一个参数(可以为空(。DUMMY_WRAPPER(x)
无效,但DUMMY_WRAPPER(x,)
和DUMMY_WRAPPER(x,y)
都可以。(这可能已在 C++20 中更改,我不确定。
如果您添加 GCC 和 Clang 拒绝编译代码-pedantic-errors
。
我还建议您使用不同的循环方法。您正在使用的那个要求您生成O(n)
代码来处理n
列表元素。
如果您稍微更改宏语法,则可以对O(1)
代码执行相同的操作:
#include <iostream>
#define STR(...) STR_(__VA_ARGS__)
#define STR_(...) #__VA_ARGS__
#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_end
#define BODY(x) [x]
#define BODY_a(x) BODY(x) BODY_b
#define BODY_b(x) BODY(x) BODY_a
#define BODY_a_end
#define BODY_b_end
#define LOOP(seq) END(BODY_a seq)
int main()
{
// Prints `[1] [2] [3]`
std::cout << STR(LOOP( (1)(2)(3) )) << 'n';
}
在这里,LOOP
可以处理任意数量的元素,而无需样板宏。
不过,它不太灵活,因为您无法从外部将任何信息传递给循环体。但这应该足以满足您的需求。
这是一个在元素之间插入逗号的版本:
#include <iostream>
#define STR(...) STR_(__VA_ARGS__)
#define STR_(...) #__VA_ARGS__
#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_end
#define BODY(x) [x]
#define BODY_0(x) BODY(x) BODY_a
#define BODY_a(x) , BODY(x) BODY_b
#define BODY_b(x) , BODY(x) BODY_a
#define BODY_0_end
#define BODY_a_end
#define BODY_b_end
#define LOOP(seq) END(BODY_0 seq)
int main()
{
// Prints `[1] , [2] , [3]`
std::cout << STR(LOOP( (1)(2)(3) )) << 'n';
}
- 将一个宏传递到另一个宏而不是直接传递内容时会出现不需要的额外"空"参数
- 模板函数,其中一个参数需要专门化,而另一个不需要
- 类介绍 (c++) 项目希望我们创建两个构造函数,但它们都不需要任何参数 - 我应该在这里做什么?
- 为什么在这种特殊情况下我不需要模板参数?
- 我需要创建一个函数在用户下打印"=",但由于变量是使用 main() 声明的,因此函数看不到参数
- std::regex_replace 不需要少于 6 个参数
- 对象将不带参数的函数调用到需要参数的函数
- 提供第二个可选参数,而不需要提供第一个可选参数
- 如何将成员函数作为参数传递给不需要它的函数?
- 通过使用不同的参数调用每个元素的非默认构造函数来初始化std::vector,而不需要进行不必要的复制
- C++函数调用后不需要的额外按引用传递参数的模式?
- 提升::池中不需要模板参数
- 函数还是函子作为模板参数?(如果不需要状态)
- 如果std::make_unique从未被调用,为什么它在默认成员初始化中不需要参数
- 纹理的SDL Renderer参数,但曲面不需要
- 在c++ 11中,是否有一种方法可以在调用以任何可调用对象(包括绑定方法)作为参数的函数时不需要模板参数
- 如何在c++中为不需要的参数使用占位符
- 为什么这个类定义中不需要空的参数列表
- 我可以使用google mock来检查方法参数而不需要预先设置期望吗?
- 传递静态方法作为参数,不需要地址操作符