Comma in C / C ++ Macro

Say we have such a macro

#define FOO(type,name) type name 

What could we use like

 FOO(int, int_var); 

But not always so simple:

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

Of course, we could do:

  typedef std::map<int, int> map_int_int_t; FOO(map_int_int_t, map_var); // OK 

which is not very ergonomic. Plus type incompatibilities should be considered. Any idea how to resolve this with a macro?

+83
c ++ c macros c-preprocessor
Dec 12 '12 at 15:00
source share
7 answers

Since angle brackets can also represent (or occur) comparison operators < , > , <= and >= , macro expansion cannot ignore commas inside angle brackets, as is done in parentheses. (This is also a problem for square brackets and curly braces, although they usually occur as balanced pairs.) You can enclose the macro argument in parentheses:

 FOO((std::map<int, int>), map_var); 

The problem is that the parameter remains in brackets inside the macro extension, which prevents it from being read as a type in most contexts.

A good trick to solve this issue is that in C ++ you can extract the type name from the name in brackets using the function type:

 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); 

Since function type generation ignores extra parentheses, you can use this macro with or without parentheses, where the type name does not contain a comma:

 FOO((int), int_var); FOO(int, int_var2); 

In C, of ​​course, this is not necessary because type names cannot contain commas outside brackets. So, for a cross-language macro, you can write:

 #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 
+90
Dec 12
source share

If you can't use parentheses and don't like the Mike SINGLE_ARG solution, just define COMMA:

 #define COMMA , FOO(std::map<int COMMA int>, map_var); 

It also helps if you want to reduce some of the macro arguments, as in

 #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); } 

which prints std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE" .

+91
Nov 07 '13 at 16:36
source share

If your preprocessor supports variable macros:

 #define SINGLE_ARG(...) __VA_ARGS__ #define FOO(type,name) type name FOO(SINGLE_ARG(std::map<int, int>), map_var); 

Otherwise, it's a little tedious:

 #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); 
+42
Dec 12 '12 at 15:08
source share

Just define FOO as

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

Then call it always with parentheses around the type argument, for example.

 FOO( (std::map<int, int>), map_var ); 

Of course, it might be a good idea to illustrate the challenges in a comment on a macro definition.

+20
Mar 14 '16 at 23:06
source share

There are at least two ways to do this. First, you can define a macro that takes several arguments:

 #define FOO2(type1, type2, name) type1, type2, name 

if you do, you will find that you end up defining more macros to handle more arguments.

Secondly, you can put a circle around the argument:

 #define FOO(type, name) type name F00((std::map<int, int>) map_var; 

if you do, you may find that additional parentheses screw the syntax of the result.

+3
Dec 12 '12 at 15:08
source share

This is possible with P99 :

 #include "p99/p99.h" #define FOO(...) P99_ALLBUTLAST(__VA_ARGS__) P99_LAST(__VA_ARGS__) FOO() 

The above code effectively breaks only the last comma in the argument list. Check with clang -E (P99 requires the C99 compiler).

+3
Dec 12 '12 at 15:25
source share

The simple answer is that you cannot. This is a side effect of choosing <...> for template arguments; < and > also displayed in unbalanced contexts, so the macro mechanism cannot be extended to handle them as if it handles parentheses. (Some committee members spoke in favor of another token, say (^...^) , but they could not convince most of the problems using <...> .)

+1
Dec 12 '12 at 15:11
source share



All Articles