C 预处理器宏:检查 token 是否已声明

发布于 2024-09-18 07:58:49 字数 912 浏览 6 评论 0原文

这是针对 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

This is for the C preprocessor experts:

How can I declare an enum with a list of some identifiers and later during the switch-statement check if an identifier was included in the list?

Example of what I need:

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

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

I thought of using a Boost sequence and expanding it into a comma separated list in the enum, but how can I check later if the sequence contains a certain token?

EDIT: What I was able to do with Boost is:

#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. */
}

That is not very beautiful, and I would prefer something like:

#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. */
}

but how could BOOST_PP_SEQ_CONTAINS be implemented?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

尽揽少女心 2024-09-25 07:58:49

你不能。 C 预处理器并不“理解”C 编程语言,它只是将其标记化。它不知道“enum”的实际含义。编译器会处理这个问题。

如果您想在预处理器中测试某些内容,则必须提供预处理器宏以供其使用。

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

You can't. The C preprocessor doesn't "understand" the C programming language, it just tokenizes it. It doesn't know what "enum" actually means. The compiler handles that.

If you want to test something in the preprocessor, then you'll have to provide preprocessor macros for it to use.

Edit: sorry, missed that you were intending to use Boost.Preprocessor. I don't know whether that can provide the necessary macros, or not, once you've involved something from Boost in the definition of your enum.

层林尽染 2024-09-25 07:58:49

我认为 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_ 前缀:

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

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

I don't think BOOST_PP_SEQ_CONTAINS can be implemented. It would require you to be able to compare two sequences of preprocessing tokens, which you can't do.

However, if you rearrange your logic a bit, you can get something closer to what you want. First, we need a couple of helper macros for use with 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)

We can define the list of enumerators and the enumeration just as you have in the original question:

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

enum Workday { BOOST_PP_SEQ_ENUM(WORKDAY_ENUMERATORS) }; 

As you can see, I've left Friday off of the list because no one actually does work on Friday. Let's consider as an example a function that returns some text describing the day of the week.

Instead of testing whether an enumerator was included in the list, we define the cases for each of the values using macros:

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

We then generate the correct case statements for the list by using the WORKDAY_ENUMERATORS and concatenating the enumerators with the WORKDAY_CASE_ prefix:

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

If a day was not included in the WORKDAY_ENUMERATORS list, no case will be generated for it.

Because we should be polite when we use the preprocessor, we then undefine the macros we used:

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

I think this is kind of ugly, but it's one way to get almost the results you are seeking.

冷…雨湿花 2024-09-25 07:58:49

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

Just don't use enum. It serves no useful purpose. Declare all your constants with #define, and use #ifdef.

腻橙味 2024-09-25 07:58:49

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

One approach is to have a great big #define or ".h" file which covers all your weekdays (the .h file has the advantage that you don't need to end all likes with backslash) and includes all the relevant information for them in macros. Then #define the generator macro to do something, invoke the big macro (or #include the header), #undef the generator macro and define it to do something else, invoke the big macro again, etc. In this scenario, one variation of the generator macro would generate something like "case ENUM_foo: func_foo(); break;". You could then write all the appropriate code for the appropriate func_* functions and they would be called as appropriate.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文