我的代码类似于
#define LIST_OF_VARIABLES \
X(value1) \
X(value2) \
X(value3)
,如https://en.wikipedia.org/wiki/X_Macro
中所述现在我需要在编译时配置LIST_OF_VARIABLES
所以它实际上可能是这样的。
#define LIST_OF_VARIABLES \
X(default_value1) \
X(cust_value2) \
X(default_value3)
或者例如
#define LIST_OF_VARIABLES \
X(default_value1) \
X(default_value2) \
X(cust_value3)
取决于之前定义的某些宏。 LIST_OF_VARIABLES很长,自定义相对较小。我不想为每个自定义复制长列表,因为这会导致维护问题(DRY原则https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)。事实上,LIST_OF_VARIABLES应该在一个文件中 其他地方的自定义(另一个文件或Makefile中的-D选项)
在伪代码中我想的是像
这样的东西#define X(arg) \
#ifdef CUST_##arg \
Y(CUST_##arg) \
#else \
Y(DEFAULT_##arg) \
#endif
然后使用名称为Y的X-macro。
但当然这不起作用,因为宏不能包含预处理器 指令。
实现这一目标的方法是什么? C是必须的(没有模板或Boost 宏),gcc的具体解决方案是可以接受的。
答案 0 :(得分:1)
我认为你要做的就是:
#ifdef USE_DEFAULT_VALUE1
#define X_DEFAULT_VALUE1 X(default_value1)
#else
#define X_DEFAULT_VALUE1 /* omitted */
#endif
#ifdef USE_DEFAULT_VALUE2
#define X_DEFAULT_VALUE2 X(default_value2)
#else
#define X_DEFAULT_VALUE2 /* omitted */
#endif
#ifdef USE_DEFAULT_VALUE3
#define X_DEFAULT_VALUE3 X(default_value3)
#else
#define X_DEFAULT_VALUE3 /* omitted */
#endif
#ifdef USE_CUST_VALUE1
#define X_CUST_VALUE1 X(cust_value1)
#else
#define X_CUST_VALUE1 /* omitted */
#endif
#ifdef USE_CUST_VALUE2
#define X_CUST_VALUE2 X(cust_value2)
#else
#define X_CUST_VALUE2 /* omitted */
#endif
#define LIST_OF_VARIABLES \
X_DEFAULT_VALUE1 \
X_DEFAULT_VALUE2 \
X_DEFAULT_VALUE3 \
X_CUST_VALUE1 \
X_CUST_VALUE2 \
然后,您需要根据您所需的特定配置定义USE_DEFAULT_VALUE1
等。
只要您始终需要相同订单的商品,这就足够了。如果您需要不同的顺序,那么您可以在不同的序列中有条件地定义LIST_OF_VARIABLES
。
答案 1 :(得分:1)
回答自己。
在评论的帮助下,我提出了一个有效且最符合要求的解决方案 我曾提到的要求
使用"主要代码"
$cat main.c
#ifndef VALUE1
#define VALUE1 value1
#endif
#ifndef VALUE2
#define VALUE2 value2
#endif
#ifndef VALUE3
#define VALUE3 value3
#endif
#define LIST_OF_VARIABLES \
X(VALUE1) \
X(VALUE2) \
X(VALUE3)
和
之类的自定义文件$cat cust1
-DVALUE2=value2cust
可以使用(GNUmake伪语法)
编译代码 $(CC) $(CFLAGS) $(shell cat cust1) main.c
实际上对单个上定义的每个值都有额外的间接性 line很好,因为它允许注释值。那不可能 可以使用单个LIST_OF_VARIABLES宏中的连续行。
修改:不正确。一个扩展到零的COMMENT(foo)宏也会解决这个问题。 (信用:从@Jonathan Leffer发布的答案中得到了想法。)
然而,该方法尚未满足我未提及的以下要求
因此我对自己的答案并不满意。需要考虑一下 来自Dr. Dobbs文章的方法多一点,也许可以使用。 打开以获得更好的答案
答案 2 :(得分:0)
如果有更多上下文,您似乎希望能够在编译时从列表中 cherry选择个别值。我想你可能对预处理器开关很感兴趣,它可以用很少的样板来完成你使用预处理器条件的工作。
这是一个简短的框架:
#define GLUEI(A,B) A##B
#define GLUE(A,B) GLUEI(A,B)
#define SECONDI(A,B,...) B
#define SECOND(...) SECONDI(__VA_ARGS__,,)
#define SWITCH(NAME_, PATTERN_, DEFAULT_) SECOND(GLUE(NAME_,PATTERN_), DEFAULT_)
调用SWITCH(MY_PREFIX_,SPECIFIC_IDENTIFIER,DEFAULT_VALUE)
将所有非匹配模式的内容展开到DEFAULT_VALUE
。 匹配模式的内容可以扩展为您映射到的任何内容。
要创建匹配模式,请定义一个名为MY_PREFIX_SPECIFIC_IDENTIFIER
的宏对象,其替换列表包含一个逗号,后跟您希望SWITCH
在此情况下展开的值。
这里的神奇之处在于SWITCH
构建了一个隐藏的令牌,让它有机会扩展(好吧,在这个实现中SECOND
的间接也很重要),然后注入一个SECOND
的新第二个参数,如果已定义的话。名义上这个新标记没有定义;在这种情况下,它只是SECOND
的第一个参数,它只是丢弃它,永远不会被再次看到。
例如,给定上述宏:
#define CONTRACT_IDENTIFIER_FOR_DEFAULT , overridden_id_for_default
#define CONTRACT_IDENTIFIER_FOR_SIGNED , overridden_id_for_signed
SWITCH(CONTRACT_IDENTIFIER_FOR_, DRAFT , draft )
SWITCH(CONTRACT_IDENTIFIER_FOR_, DRAWN , drawn )
SWITCH(CONTRACT_IDENTIFIER_FOR_, PROOFED , proofed )
SWITCH(CONTRACT_IDENTIFIER_FOR_, DELIVERED , delivered )
SWITCH(CONTRACT_IDENTIFIER_FOR_, SIGNED , signed )
SWITCH(CONTRACT_IDENTIFIER_FOR_, FULFILLED , fulfilled )
SWITCH(CONTRACT_IDENTIFIER_FOR_, DEFAULT , default )
...将扩展为:
draft
drawn
proofed
delivered
overridden_id_for_signed
fulfilled
overridden_id_for_default
假设您希望提供值名称,并简单地从命令行替换cherry pick值,您可以使用SWITCH
执行以下操作:
#define VARVALUE(N_,V_) SWITCH(VALUE_FOR_, N_, V_)
#define LIST_OF_VARIABLES \
X(VARVALUE(value1, default_value1)) \
X(VARVALUE(value2, default_value2)) \
X(VARVALUE(value3, default_value3))
将首先在此表单中应用VARVALUE宏。要覆盖特定值,可以使用#define:
定义模式匹配器#define VALUE_FOR_value2 , custom_value2
...或在命令行/ makefile:
CFLAGS += -DVALUE_FOR_value2=,custom_value2
要支持安全地禁用单个项目,请嵌套两个开关并添加EAT宏以捕获条目:
#define EAT(...)
#define SELECT_ITEM_MACRO_FOR_STATE_ON , X
#define X_IF_ENABLED(N_, V_) \
SWITCH(SELECT_ITEM_MACRO_FOR_STATE_, SWITCH(ENABLE_VALUE_, N_, ON), EAT) \
(SWITCH(VALUE_FOR_, N_, V_))
#define LIST_OF_VARIABLES \
X_IF_ENABLED(value1, default_value1) \
X_IF_ENABLED(value2, default_value2) \
X_IF_ENABLED(value3, default_value3)
与以前一样,可以使用VALUE_FOR_valuex
模式宏覆盖单个宏,但这也允许使用ENABLE_VALUE_valuex
宏禁用项目,除了,ON
之外,可以将其设置为禁用项目
同样,添加对插入值的支持的一种方法是改变想法:
#define ADD_ITEM_MACRO_FOR_STATE_EAT , EAT
#define X_IF_ADDED(N_) \
SWITCH(ADD_ITEM_MACRO_FOR_STATE_, SWITCH(VALUE_FOR_, N_, EAT), X) \
(SECOND(GLUE(VALUE_FOR_,N_)))
#define LIST_OF_VARIABLES \
X_IF_ENABLED(value1, default_value1) \
X_IF_ENABLED(value2, default_value2) \
X_IF_ENABLED(value3, default_value3) \
X_IF_ADDED(value4) \
X_IF_ADDED(value5) \
X_IF_ADDED(value6)
...这允许您将VALUE_FOR_value4
定义为模式宏,但默认情况下会扩展为空。
支持设置,删除或插入值的框架最终成为:
#define GLUEI(A,B) A##B
#define GLUE(A,B) GLUEI(A,B)
#define SECONDI(A,B,...) B
#define SECOND(...) SECONDI(__VA_ARGS__,,)
#define SWITCH(NAME_, PATTERN_, DEFAULT_) SECOND(GLUE(NAME_,PATTERN_), DEFAULT_)
#define EAT(...)
#define SELECT_ITEM_MACRO_FOR_STATE_ON , X
#define X_IF_ENABLED(N_, V_) \
SWITCH(SELECT_ITEM_MACRO_FOR_STATE_, SWITCH(ENABLE_VALUE_, N_, ON), EAT) \
(SWITCH(VALUE_FOR_, N_, V_))
#define ADD_ITEM_MACRO_FOR_STATE_EAT , EAT
#define X_IF_ADDED(N_) \
SWITCH(ADD_ITEM_MACRO_FOR_STATE_, SWITCH(VALUE_FOR_, N_, EAT), X) \
(SECOND(GLUE(VALUE_FOR_,N_)))
鉴于此框架,您的列表宏将包含一系列X(value)
,X_IF_ENABLED(name,default_value)
和/或X_IF_ADDED(name)
值,其中:
X(value)
可用于始终使用值X_IF_ENABLED(name,default_value)
将使用default_value调用X,允许您根据名称覆盖默认值。X_IF_ADDED(name)
将提供一个"空插槽"使用名称,除非您覆盖该位置,否则将不执行任何操作。通过定义VALUE_FOR_name
扩展到,replacement
来完成覆盖广告位。通过将ENABLE_VALUE_name
定义为,OFF
来启用已启用的广告位。