C/C++宏中的逗号

PoP*_*PoP 93 c c++ macros c-preprocessor

假设我们有这样的宏

#define FOO(type,name) type name
Run Code Online (Sandbox Code Playgroud)

我们可以使用哪个

FOO(int, int_var);
Run Code Online (Sandbox Code Playgroud)

但并不总是那么简单:

FOO(std::map<int, int>, map_var); // error: macro "FOO" passed 3 arguments, but takes just 2
Run Code Online (Sandbox Code Playgroud)

我们当然可以这样做:

 typedef std::map<int, int> map_int_int_t;
 FOO(map_int_int_t, map_var); // OK
Run Code Online (Sandbox Code Playgroud)

这不是非常符合人体工程学的.必须处理类型不兼容的问题.知道如何用宏来解决这个问题吗?

not*_*ser 108

如果你不能使用括号而你不喜欢Mike的SINGLE_ARG解决方案,那么只需定义一个COMMA:

#define COMMA ,

FOO(std::map<int COMMA int>, map_var);
Run Code Online (Sandbox Code Playgroud)

如果您想要对某些宏参数进行字符串化,这也会有所帮助,如

#include <cstdio>
#include <map>
#include <typeinfo>

#define STRV(...) #__VA_ARGS__
#define COMMA ,
#define FOO(type, bar) bar(STRV(type) \
    " has typeid name \"%s\"", typeid(type).name())

int main()
{
    FOO(std::map<int COMMA int>, std::printf);
}
Run Code Online (Sandbox Code Playgroud)

打印std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE".

  • 再加上1恐怖 (21认同)
  • #define COMMA哇,你刚刚为我节省了工作时间......为什么我多年前没有想到这一点.谢谢你分享这个想法.这甚至允许我构建宏,这些宏设置具有不同参数计数的函数. (11认同)
  • @kiw如果你 `#define STRVX(...) STRV(__VA_ARGS__)` 和 `#define STRV(...) # __VA_ARGS__`,则 `std::cout &lt;&lt; STRV(type&lt;A COMMA B&gt;) &lt; &lt; std::endl;` 将打印 `type&lt;A COMMA B&gt;` 和 `std::cout &lt;&lt; STRVX(type&lt;A COMMA B&gt;) &lt;&lt; std::endl;` 将打印 `type&lt;A , B &gt;`。(“STRV”代表“可变参数字符串化”,“STRVX”代表“扩展可变参数字符串化”。) (2认同)

eca*_*mur 100

因为尖括号也可以代表(或发生在)比较运算符<,>,<=>=,宏扩展不能像它括号内确实忽略角括号内的逗号.(对于方括号和大括号,这也是一个问题,即使它们通常作为平衡对出现.)您可以将宏参数括在括号中:

FOO((std::map<int, int>), map_var);
Run Code Online (Sandbox Code Playgroud)

问题是该参数在宏扩展中保持括号,这可以防止在大多数上下文中将其作为类型读取.

解决此问题的一个很好的技巧是在C++中,您可以使用函数类型从带括号的类型名称中提取类型名称:

template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
FOO((std::map<int, int>), map_var);
Run Code Online (Sandbox Code Playgroud)

因为形成函数类型会忽略额外的括号,所以您可以使用带有或不带括号的宏,其中类型名称不包含逗号:

FOO((int), int_var);
FOO(int, int_var2);
Run Code Online (Sandbox Code Playgroud)

当然,在C语言中,这不是必需的,因为类型名称不能包含括号外的逗号.因此,对于跨语言宏,您可以编写:

#ifdef __cplusplus__
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
#else
#define FOO(t,name) t name
#endif
Run Code Online (Sandbox Code Playgroud)


Mik*_*our 48

如果您的预处理器支持可变参数宏:

#define SINGLE_ARG(...) __VA_ARGS__
#define FOO(type,name) type name

FOO(SINGLE_ARG(std::map<int, int>), map_var);
Run Code Online (Sandbox Code Playgroud)

否则,它有点单调乏味:

#define SINGLE_ARG2(A,B) A,B
#define SINGLE_ARG3(A,B,C) A,B,C
// as many as you'll need

FOO(SINGLE_ARG2(std::map<int, int>), map_var);
Run Code Online (Sandbox Code Playgroud)

  • @VladLazarenko:因为你不能总是在括号中放置任意代码.特别是,您不能在声明符中的类型名称周围添加括号,这正是此参数的变化. (14认同)
  • ...还因为您可能只能修改宏 _definition_ 而不是所有调用它的地方(这可能不受您的控制,或者可能分布在 1000 个文件中,等等)。例如,当添加宏以从同名函数接管职责时,就会发生这种情况。 (2认同)

Che*_*Alf 27

只需定义FOO

#define UNPACK( ... ) __VA_ARGS__

#define FOO( type, name ) UNPACK type name
Run Code Online (Sandbox Code Playgroud)

然后使用类型参数周围的括号来调用它,例如

FOO( (std::map<int, int>), map_var );
Run Code Online (Sandbox Code Playgroud)

在宏定义的注释中举例说明调用当然是个好主意.

  • 而且因为它也很难理解它的工作原理和原因 (4认同)
  • @iFreilicht:一年后发布了一段时间.;-) (2认同)