Conditional definition of elements in an X Macro

我是研究僧i 提交于 2019-12-19 21:54:13

问题


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?


回答1:


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



回答2:


There are a lot of different approaches to this.

The first one that comes to mind is, instead of one X macro, have several X macros that are conditionally defined. Then have a master X macro that calls all of them. This might still give you reasonably clear code. However, it may scale up badly if the number of conditions is large.

The second one that comes to mind is, don't use X macros, use Boost::preprocessor instead. There are good things and bad things about this, here's an example of what it might look like:

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

#include <iostream>

#define CONDITION 0
// #define CONDITION 1

#define MY_FIRST_SEQ (foo)(bar)(baz)
#define MY_SECOND_SEQ BOOST_PP_IF(CONDITION, (zip), (quz))
#define MY_SEQ MY_FIRST_SEQ MY_SECOND_SEQ


// Now we adapt original example
// The parameters r, data need to be there but don't have to be used, see here:
// http://www.boost.org/doc/libs/1_62_0/libs/preprocessor/doc/ref/seq_for_each.html
#define STR(x) #x
#define PRINT_X(r, data, E) void BOOST_PP_CAT(print_, E) () { std::cout << STR(E); };


// Now do this where we would invoke the X_MACRO
BOOST_PP_SEQ_FOR_EACH(PRINT_X, _, MY_SEQ)

int main() {}



回答3:


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


来源:https://stackoverflow.com/questions/46019370/conditional-definition-of-elements-in-an-x-macro

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!