Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Preprocessor macro loop __VA_ARGS__ 1 vs 2+ arguments

Tags:

c++

macros

I'm using the macros from this post looping through my arguments. Everything works great! However, is there a way to combine these two CCB_CREATE and CCB_CREATE_MORE?

I need to extract the first argument object_type to write additional code. The additional object_types will be using the FOR_EACH loop to insert into the map.

The compiler complaints when I only have one argument when using CCB_CREATE_MORE(Type1). To fix that I made another macro to handle that CCB_CREATE(Type1). Hoping to find a clever solution to combine these two into one elegant macro. Any ideas?

#define INSERT_LOADER_MAP(object_type) loader_map.insert(make_pair(#object_type, object_type##Loader::loader()))


#define CCB_CREATE_MORE(object_type,...) \
static CCNode * create##object_type##Node() { \
    std::map<std::string, CCNodeLoader*> loader_map; \
    std::string classname = #object_type; \
    FOR_EACH(INSERT_LOADER_MAP,object_type,__VA_ARGS__); \
    return loadCCBFile((classname + ".ccbi").c_str(), loader_map); \
}


#define CCB_CREATE(object_type) \
static CCNode * create##object_type##Node() { \
    std::map<std::string, CCNodeLoader*> loader_map; \
    std::string classname = #object_type; \
    INSERT_LOADER_MAP(object_type); \
    return loadCCBFile((classname + ".ccbi").c_str(), loader_map); \
}
like image 440
docchang Avatar asked Oct 18 '13 02:10

docchang


People also ask

How many arguments can a macro Canhave?

A comma must separate each parameter. For portability, you should not have more than 31 parameters for a macro. The parameter list may end with an ellipsis (…).

Can you supply more than one argument in a macro call?

3.3 Macro Arguments To invoke a macro that takes arguments, you write the name of the macro followed by a list of actual arguments in parentheses, separated by commas. The invocation of the macro need not be restricted to a single logical line—it can cross as many lines in the source file as you wish.

What is the correct format for naming a macro with multiple arguments?

To create a macro with arguments, put them in parentheses separated by commas after the macro name, e.g. then BadSquare(3+4) would give 3+4*3+4, which evaluates to 19, which is probably not what we intended.

What is __ Va_args __ C++?

macro expansion possible specifying __VA_ARGS__ The '...' in the parameter list represents the variadic data when the macro is invoked and the __VA_ARGS__ in the expansion represents the variadic data in the expansion of the macro. Variadic data is of the form of 1 or more preprocessor tokens separated by commas.


2 Answers

The compiler is likely complaining about the trailing comma when the variadic arguments list is empty. GCC and Visual Studio compilers support the non-standard extension ##__VA_ARGS__ to suppress the trailing comma:

#define FOO(fmt, ...) printf(fmt, ##__VA_ARGS__)

The Visual Studio compilers will also suppress the trailing comma even without the ## extension.

See GCC documentation here, and Visual Studio documentation here.

If you need a standards-compliant solution, there is one detailed in an answer to this question.

So if you are using either gcc or Visual Studio, you should be able to use your original macro with this simple change:

#define CCB_CREATE(object_type,...) \
static CCNode * create##object_type##Node() { \
    std::map<std::string, CCNodeLoader*> loader_map; \
    std::string classname = #object_type; \
    FOR_EACH(INSERT_LOADER_MAP,object_type,##__VA_ARGS__); \
    return loadCCBFile((classname + ".ccbi").c_str(), loader_map); \
}

Edit: You would need to use the ##__VA_ARGS__ extension in the FOR_EACH() macro as well, or the more elegant modification suggested by ugoren.

#define FOR_EACH(what, x, ...) FOR_EACH_(FOR_EACH_NARG(x, ##__VA_ARGS__), what, x, __VA_ARGS__)
like image 117
Chris Olsen Avatar answered Oct 01 '22 21:10

Chris Olsen


In addition to Chris Olsen's suggestion, a slight change to the FOR_EACH macro is needed:

#define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__)

As a result, FOR_EACH(X, a) will become X(a) (instead of X(a); X();). This eliminates an empty INSERT_LOADER_MAP invocation.

like image 42
ugoren Avatar answered Oct 01 '22 23:10

ugoren