【问题标题】:C preprocessor macros: check if token was declaredC 预处理器宏:检查是否声明了令牌
【发布时间】:2011-04-08 11:44:58
【问题描述】:

这是给 C 预处理器专家的:

如何声明带有一些标识符列表的enum,然后在 switch 语句检查列表中是否包含标识符?

我需要的示例:

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

【问题讨论】:

  • 我删除了 C 标记,因为你也标记了 boost。
  • Boost 预处理器宏应该独立于 C++ 扩展,因此它们也可以在 C 中使用。

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


【解决方案1】:

你不能。 C 预处理器不“理解”C 编程语言,它只是将它标记化。它不知道“枚举”实际上是什么意思。编译器会处理它。

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

编辑:抱歉,错过了您打算使用 Boost.Preprocessor。一旦您在枚举的定义中涉及到 Boost 中的某些内容,我不知道这是否可以提供必要的宏。

【讨论】:

    【解决方案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 并将枚举器与WORKDAY_CASE_ 前缀连接起来,为列表生成正确的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
    

    我认为这有点丑陋,但这是获得几乎您所寻求的结果的一种方法。

    【讨论】:

    • 您是否还知道如何让 Boost 输出为每个案例开始一个新行?
    • @Thomas:不,但我不知道为什么这很重要:在评估预处理指令之后,换行符并不重要。
    • 这可以在 gcc 打印出错误时帮助我(当行长度为 20000 个字符时,可能很难发现像“'('之前的预期标识符”这样的错误)
    【解决方案3】:

    只是不要使用enum。它没有任何用处。使用#define 声明所有常量,并使用#ifdef

    【讨论】:

    • 即使在纯 C 语言中,enum#define 相比也有一个很大的优势:用enum 定义的常量对于调试器来说是已知的,但用#define 定义的常量通常不知道.不太重要但仍然有用的是,如果您忘记在 switch 语句中包含 enum 的某些值,编译器有足够的信息来对您大喊大叫。在 C++ 中,您还可以受益于常量实际上是它们自己的类型。
    • 我会告诉你的,但这仍然是一个实施问题。如果编译器和调试器的实现足够聪明,他们可以知道#define,就像enum一样。另一方面,就实际的 C 语言而言,#define 严格来说比enum 更强大。您可以使用 #define 完成一些有用的事情,而使用 enum 无法完成,但是您无法使用 enum 完成任何事情(在实际语言的范围内,而不是调试器或特定于实现的事情)你不能用#define
    • 我正在用 C++ 编写代码,并将我的枚举放在类或命名空间中,这在 define 中是不可能的。
    • 好的,那么你有充分的理由使用enum。我只是希望每个人都停止将 C++ 问题标记为 C 假装它们是同一种语言...
    • 你可以用enum 常量做的一件事是你不能用#define 做的——有一个结构或联合标签和/或同名的成员。
    【解决方案4】:

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

    【讨论】:

    • 你能发布一个简短的代码示例吗?这听起来像是詹姆斯对我的建议。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-23
    • 1970-01-01
    • 2016-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多