8

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); \
}
Community
  • 1
  • 1
docchang
  • 1,115
  • 15
  • 32

2 Answers2

7

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__)
Community
  • 1
  • 1
Chris Olsen
  • 3,243
  • 1
  • 27
  • 41
2

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.

Community
  • 1
  • 1
ugoren
  • 16,023
  • 3
  • 35
  • 65