想象一下,我有一个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_*
宏的每个可能组合提供一个列表。
我怎样才能以合理的方式做到这一点?
答案 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_ZIP
是0
还是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))