X宏中元素的条件定义

时间:2017-09-03 00:45:36

标签: c++ macros c-preprocessor

想象一下,我有一个X Macro的项目列表定义如下:

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

这很好用,我可以调用它来生成为每个元素模板化的相同代码,例如:

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

为每个X_MACRO元素生成void print_foo() { std::cout << "foo"; };等函数。到目前为止,非常好。

但是,现在,我希望X宏元素列表以预处理器宏为条件。例如,zip元素只应在定义USE_ZIP时包含在X宏中。当然,我无法在X宏中放置#ifdef,例如:

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

我可以改为将列表写两次,一次使用zip,一次不使用,基于USE_ZIP,如此:

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

...但这违反了DRY,更重要的是,如果您需要有条件地包含其他元素,它会迅速失控,这需要为USE_*宏的每个可能组合提供一个列表。

我怎样才能以合理的方式做到这一点?

3 个答案:

答案 0 :(得分:5)

这样做的一种方法是以 base 样式拆分并从超级宏调用它(我不知道它们是否有特殊名称):

#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

它并不完美,但它仍然可能有用: - )

另一个巧妙的诀窍是拥有一个简单的条件宏(比如USE_ZIP0还是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

然后你可以说:

#define X_MACRO(fn) \
    fn(foo) \
    fn(bar) \
    IF(USE_ZIP, fn(zip))

答案 1 :(得分:2)

有很多不同的方法。

首先想到的是,有一个X宏,而不是一个X宏,有几个有条件定义的X宏。然后有一个调用所有这些的主X宏。这仍然可以为您提供合理清晰的代码。但是,如果条件数量很大,它可能会严重扩大。

第二个想到的是,不要使用X宏,而是使用Boost :: preprocessor。这有好事和坏事,这里有一个例子:

#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() {}

答案 2 :(得分:1)

怎么样:

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