Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comma in C/C++ macro

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

#define COMMA ,

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

This also helps if you want to stringify 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".


Because angle brackets can also represent (or occur in) the comparison operators <, >, <= and >=, macro expansion can't ignore commas inside angle brackets like it does within parentheses. (This is also a problem for square brackets and braces, even though those usually occur as balanced pairs.) You can enclose the macro argument in parentheses:

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

The problem is then that the parameter remains parenthesized inside the macro expansion, which prevents it being read as a type in most contexts.

A nice trick to workaround this is that in C++, you can extract a typename from a parenthesized type name using a 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);

Because forming function types ignores extra parentheses, you can use this macro with or without parentheses where the type name doesn't include a comma:

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

In C, of course, this isn't necessary because type names can't contain commas outside parentheses. 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

If your preprocessor supports variadic 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 bit more 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);

Just define FOO as

#define UNPACK( ... ) __VA_ARGS__

#define FOO( type, name ) UNPACK type name

Then invoke it always with parenthesis around the type argument, e.g.

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

It can of course be a good idea to exemplify the invocations in a comment on the macro definition.


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

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

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

Second, you can put parentheses around the argument:

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

if you do that you may find that the extra parentheses screw up the syntax of the result.


This is possible with P99:

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

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