C预处理器宏:检查是否声明了令牌

时间:2010-09-05 16:29:39

标签: c++ boost c-preprocessor boost-preprocessor

这适用于 C 预处理专家:

如何在enum中声明一些标识符列表,稍后在switch-statement检查中是否列出了标识符?

我需要的例子:

typedef enum { e1, e2, e3, e4, e5, e6 } e;

e x;
switch (x) {
#if DECLARED_IN_ENUM (e1)
  case e1 : ...
#endif
  /* etc. */
}

我想过使用Boost序列并将其扩展为枚举中的逗号分隔列表,但是如果序列包含某个令牌,我该如何检查呢?

编辑:我能用Boost做的是:

#define e1 e1
#define e2 e2
#define e3 e3
#define e4 e4
#define e5 e5
#define e6 e6
#define E (e1)(e2)(e3)(e4)(e5)(e6)

typedef enum { BOOST_PP_SEQ_ENUM(E) } e;

e x;
switch (x) {
#if defined (e1)
  case e1 : ...
#endif
  /* etc. */
}

那不是很漂亮,我更喜欢这样的东西:

#define E (e1)(e2)(e3)(e4)(e5)(e6)

typedef enum { BOOST_PP_SEQ_ENUM(E) } e;

e x;
switch (x) {
#if BOOST_PP_SEQ_CONTAINS (e1,E)
  case e1 : ...
#endif
  /* etc. */
}

但是如何实施BOOST_PP_SEQ_CONTAINS

4 个答案:

答案 0 :(得分:4)

你做不到。 C预处理器不“理解”C编程语言,它只是标记它。它不知道“枚举”究竟意味着什么。编译器处理。

如果你想在预处理器中测试一些东西,那么你必须提供预处理器宏供它使用。

编辑:抱歉,错过了您打算使用Boost.Preprocessor。我不知道是否可以提供必要的宏,一旦你在enum的定义中包含了Boost的内容。

答案 1 :(得分:2)

我认为BOOST_PP_SEQ_CONTAINS无法实施。这将要求您能够比较两个预处理令牌序列,这是您无法做到的。

但是,如果你稍微重新排列逻辑,你可以得到更接近你想要的东西。首先,我们需要一些辅助宏来与BOOST_PP_SEQ_FOR_EACH一起使用:

#include <boost/preprocessor.hpp>

// General purpose macros:
#define EXPAND_ENUM_CASE_2(text1, text2) text1 ## text2
#define EXPAND_ENUM_CASE(r, data, elem) \
    case elem : EXPAND_ENUM_CASE_2(data ## _ ## CASE ## _ , elem)

我们可以像原始问题一样定义枚举器列表和枚举:

#define WORKDAY_ENUMERATORS (Monday)(Tuesday)(Wednesday)(Thursday)

enum Workday { BOOST_PP_SEQ_ENUM(WORKDAY_ENUMERATORS) }; 

正如你所看到的,我已经离开星期五,因为周五没有人真正工作。让我们考虑一个函数,它返回一些描述星期几的文本。

我们不是测试枚举器是否包含在列表中,而是使用宏定义每个值的个案:

#define WORKDAY_CASE_Monday    { return "Mondays suck";                     }
#define WORKDAY_CASE_Tuesday   { return "Tuesdays are better than Mondays"; }
#define WORKDAY_CASE_Wednesday { return "Hooray for humpday!";              }
#define WORKDAY_CASE_Thursday  { return "Thursdays are okay";               }
#define WORKDAY_CASE_Friday    { return "No one really works on Friday";    }

然后,我们使用WORKDAY_ENUMERATORS生成列表的正确case语句,并将枚举数与WORKDAY_CASE_前缀连接起来:

const char* get_day_text(Workday d)
{    
    switch (d)
    {
        BOOST_PP_SEQ_FOR_EACH(EXPAND_ENUM_CASE, WORKDAY, WORKDAY_ENUMERATORS)
    }
    return "WTF?!  That's not a workday!";
}

如果WORKDAY_ENUMERATORS列表中未包含某一天,则不会为其生成任何案例。

因为我们在使用预处理器时应该礼貌,然后我们取消定义我们使用的宏:

#undef WORKDAY_CASE_Monday
#undef WORKDAY_CASE_Tuesday
#undef WORKDAY_CASE_Wednesday
#undef WORKDAY_CASE_Thursday
#undef WORKDAY_CASE_Friday

我认为这有点难看,但这是获得几乎所有搜索结果的一种方法。

答案 2 :(得分:0)

请勿使用enum。它没有任何用处。使用#define声明所有常量,并使用#ifdef

答案 3 :(得分:0)

一种方法是拥有一个很棒的大#define或“.h”文件,它涵盖你所有的工作日(.h文件的优点是你不需要用反斜杠结束所有喜欢)并且包括所有相关的宏中的信息。然后#define生成器宏做某事,调用大宏(或#include头),#undef生成器宏并定义它做其他事情,再调用大宏等等。在这种情况下,一个变体生成器宏将生成类似“case ENUM_foo:func_foo(); break;”的内容。然后,您可以为适当的func_ *函数编写所有适当的代码,并在适当时调用它们。