Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional definition of elements in an X Macro

Imagine I have an X Macro for a list of items defined something like this:

#define X_MACRO(FN) \
  FN(foo) \
  FN(bar) \
  FN(zip)

This works great and I can call it to generate the same code templatized for each element, like:

#define xstr(s) str(s)
#define str(s) #s
#define PRINT_X(E) void print_ ## E () { std::cout << str(E); }; 
X_MACRO(PRINT_X)

This generates functions like void print_foo() { std::cout << "foo"; }; for each of the X_MACRO elements. So far, so good.

Now, however, I want the list of X Macro elements to be conditional on a pre-processor macro. For example the zip element should only be included in the X Macro if USE_ZIP is defined. Of course, I can't put an #ifdef inside the X Macro, like:

#define X_MACRO(FN) \
  FN(foo) \
  FN(bar) \
#ifdef USE_ZIP
  FN(zip)
#endif

I could instead write the list twice, once with zip and once without, based on the USE_ZIP like so:

#ifdef USE_ZIP
#define X_MACRO(FN) \
  FN(foo) \
  FN(bar) \
  FN(zip)
#else
#define X_MACRO(FN) \
  FN(foo) \
  FN(bar)
#endif

... but this violates DRY and more importantly it rapidly spirals out of control if you need to conditionally include other elements, which would require a list for each possible combination of USE_* macros.

How can I do this in a reasonable way?

like image 784
BeeOnRope Avatar asked Dec 13 '22 21:12

BeeOnRope


2 Answers

One way to do this is to split things in a base style and call it from a super macro (I don't know if these have special names):

#define X_MACRO_BASE(fn) \
    fn(foo) \
    fn(bar) \

#if USE_ZIP

#define X_MACRO(fn) \
    X_MACRO_BASE(fn) \
    fn(zip)

#else

#define X_MACRO(fn) \
    X_MACRO_BASE(fn)

#endif

It's not perfect, but it still might be useful :-)


Another neat trick is to have a simple conditional macro (say if USE_ZIP was 0 or 1):

#define IF(cond, foo) IF_IMPL(cond, foo)
#define IF_IMPL(cond, foo) IF_ ## cond (foo)
#define IF_0(foo)
#define IF_1(foo) foo

Then you can say:

#define X_MACRO(fn) \
    fn(foo) \
    fn(bar) \
    IF(USE_ZIP, fn(zip))
like image 179
Travis Gockel Avatar answered Dec 23 '22 12:12

Travis Gockel


How about:

#ifdef USE_ZIP
#  define IF_ZIP(arg) arg
#else
#  define IF_ZIP(arg)
#endif

#define X_MACRO(MAC) \
  MAC(foo)           \
  MAC(bar)           \
  IF_ZIP(MAC(zip))
like image 27
HTNW Avatar answered Dec 23 '22 10:12

HTNW