fra*_*i87 2 c c++ c-preprocessor
最初这必须是另一个问题的最后一段(这一个),但我注意到主题不是那么相似,因此我想发布另一个问题.
我想要完成的是制作一个自动化系统,以简单的方式编译一些动作.
例如,而不是写作
switch (var)
{
case SM_1:
printf("Case 1");
break;
case SM_2:
printf("Case 2");
break;
case SM_3:
printf("Case 3");
break;
}
Run Code Online (Sandbox Code Playgroud)
我会更容易写出类似的东西
#define CASES
#define CreateCaseX(s,l) CASES += \
case s: \
printf(l); \
break;
CreateCaseX(SM_1, "Case 1")
CreateCaseX(SM_2, "Case 2")
CreateCaseX(SM_3, "Case 3")
...
switch(var)
{
CASES
}
Run Code Online (Sandbox Code Playgroud)
我不希望该CASES +=语句实际工作,但是......是否有一些类似的构造我可以在预处理器中使用一些动作(函数调用,切换案例......)添加到列表然后让它们写入它们一次全部?
编辑:我认为我已经很好地解决了我的问题边界,但是看一些答案和评论我注意到这个问题可以用不同的方式解释.所以请原谅我,现在我会尝试更好地详细说明.
我正在写一个简单的状态机.每当我添加状态时,我都必须执行不同的代码修改,例如
例如,我可以在我的文件中包含以下行:
enum {
SM_Case_1,
SM_Case_2,
SM_Case_3
} currentState;
void printState()
{
switch (currentState)
{
case SM_Case_1:
printf("State 1");
break;
case SM_Case_2:
printf("State 2");
break;
case SM_Case_3:
printf("State 3");
break;
}
}
void executeAction()
{
switch (currentState)
{
case SM_Case_1:
// Do nothing
break;
case SM_Case_2:
globalVariable += 10;
break;
case SM_Case_3:
printf("Error");
break;
}
}
Run Code Online (Sandbox Code Playgroud)
在我看来,有一个宏处理这个更容易(并且更容易维护),例如:
#define ENUMS
#define NAMES_CASES
#define ACTION_CASES
#define CreateState(s,l,a) \
ENUMS += s, \
NAMES_CASES += case s: printf(l); break; \
ACTION_CASES += case s: a; break;
CreateState(SM_Case_1,"State 1",{})
CreateState(SM_Case_2,"State 2",globalVariable += 10)
CreateState(SM_Case_3,"State 3",printf("Error"))
enum {
ENUMS
} currentState;
void printState()
{
switch (currentState)
{
NAMES_CASES
}
}
void executeAction()
{
switch (currentState)
{
ACTION_CASES
}
}
Run Code Online (Sandbox Code Playgroud)
我注意到编写一个可行的语法很棘手,所以可能这样的技术不存在,但是......如果有的话,知道它是有用的.
没有在宏中累积字符串的一般方法,但您可以通过将宏调用序列构建为宏来有效地定义列表.这必须在一个部分完成,但似乎这适用于你的例子.
一旦构建了列表,就可以多次使用它以用于不同的目的.该技术通常称为X宏.
这是一个简单的例子.(我个人更喜欢将宏名称传递给X宏的样式,而不是X像维基百科文章那样对宏名称进行硬编码.)
// Make the list (I added semicolons to the actions.)
#define STATES(X) \
X(SM_Case_1,"State 1",{}) \
X(SM_Case_2,"State 2",globalVariable += 10;) \
X(SM_Case_3,"State 3",printf("Error");)
//...
#define ENUM(S, L, A) S,
enum {
STATES(ENUM)
} currentState;
#undef ENUM
// ...
#define CASE(S, L, A) case S: printf("%s", L); break;
void printState() {
switch (currentState) {
STATES(CASE);
}
}
#undef CASE
// ...
#define CASE(S, L, A) case S: { A } break;
void executeAction() {
switch (currentState) {
STATES(CASE);
}
}
#undef CASE
Run Code Online (Sandbox Code Playgroud)
请注意,后两个节足够相似,它们可以在宏中封装,如:
#define SWITCH_FUNC(NAME, CASE) \
void NAME() { switch (currentState) { STATES(CASE); } }
Run Code Online (Sandbox Code Playgroud)
此外,您可以使用标记串联和字符串化将列表简化为两个参数而不是三个:
#define STATES(X) \
X(State_1, {}) \
X(State_2, globalVariable += 10;) \
X(State_3, printf("Error");)
#define CONCAT_(A, B) A##B
#define STRINGIFY_(S) #S
#define CASENAME(S) CONCAT_(SM_, S)
#define ENUM(S,A) CASENAME(S),
#define PRINT_CASE(S,A) case CASENAME(S): printf("%s", STRINGIFY_(S)); break;
#define ACTION_CASE(S,A) case CASENAME(S): { A } break;
Run Code Online (Sandbox Code Playgroud)
(在coliru上运行,虽然预处理器输出有点难以阅读)
有时,您会希望使用预处理器条件来控制是否将单个项目放入列表中.请参阅本答案的第二部分,以获得实现这一目标的精彩技巧.