预处理器中的命令串联

时间:2018-03-24 15:19:00

标签: c++ c c-preprocessor

最初这必须是另一个问题的最后一段(this one),但我注意到主题不是那么相似,因此我想发布另一个问题。

我要做的是创建一个自动化系统,以简单的方式编译一些操作。

例如,而不是写

switch (var)
{
    case SM_1:
        printf("Case 1");
        break;
    case SM_2:
        printf("Case 2");
        break;
    case SM_3:
        printf("Case 3");
        break;
}

我会更容易写出像

这样的东西
#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
}

我不希望CASES +=语句实际工作,但是......是否有一些类似的构造我可以在预处理器中使用来添加一些动作(函数调用,切换案例......)列表,然后让它一次写出来?

编辑:我认为我已经很好地解决了我的问题边界,但是看一些答案和评论,我注意到这个问题可以用不同的方式来解释。所以请原谅我,现在我会尝试更好地详细说明。

我正在写一个简单的状态机。每当我添加状态时,我都必须执行不同的代码修改,例如

  • 将状态添加到枚举
  • 在switch case结构中添加状态名称,该结构返回当前状态的名称
  • 添加当该状态为当前状态时要执行的操作

例如,我可以在我的文件中包含这些行:

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;
    }
}

我认为有一个宏处理这个问题会更容易(也更容易维护),例如:

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

我注意到编写一个可行的语法很棘手,所以可能这样的技术不存在,但是......如果有的话,知道它是有用的。

2 个答案:

答案 0 :(得分:5)

在宏中没有通用的方法来累积字符串,但是您可以通过将宏调用序列构建为宏来有效地定义列表。这必须在一个部分完成,但似乎这适用于你的例子。

构建完列表后,您可以多次使用它以达到不同的目的。这种技术通常被称为X macros

这是一个简单的例子。 (我个人更喜欢将宏名称传递给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

请注意,后两个节很相似,可以在宏中包含:

#define SWITCH_FUNC(NAME, CASE) \
  void NAME() { switch (currentState) { STATES(CASE); } }

此外,您可以使用标记连接和字符串化将列表简化为两个参数而不是三个:

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

Live on coliru,虽然预处理器输出有点难以阅读)

有时,您会希望使用预处理器条件来控制是否将单个项目放入列表中。请参阅this answer的第二部分,以获得完成此任务的绝佳技巧。

答案 1 :(得分:2)

你不能不知道这个有效代码的区别是什么?

  ClientCustomer customerObj = autoMapper.Map<ClientCustomer>(customer);

只需使用3个宏定义#include <stdio.h> #define SM_1 1 #define SM_2 2 #define SM_3 3 #define CreateCaseX(s,l) case s: \ printf(l); \ break #define CASES \ CreateCaseX(SM_1, "Case 1"); \ CreateCaseX(SM_2, "Case 2"); \ CreateCaseX(SM_3, "Case 3") void test(int var) { switch(var) { CASES; } } 即可。这是您可以获得的最接近的列表。