【问题标题】:Check existence of an entry in C pre-processor list检查 C 预处理器列表中是否存在条目
【发布时间】:2018-02-27 02:30:04
【问题描述】:

是否可以检查 X-macro 定义的列表中是否存在条目?鉴于下面的示例代码,我希望 #if defined(GEORGE) 条件为真。

编辑:当然没有明确的#define GEORGE。我希望有一种方法来检查列表中的条目(在预处理器中),我只想在列表中进行声明。这可能是不可能的,但我想我问了。

谢谢!

#include <stdio.h>

#define NAMES \
X( JOHN, "John Adams" ) \
X( GEORGE, "George Washington" ) \
X( ABRAHAM, "Abraham Lincoln ")

#define X(_id, _name)    _id,
typedef enum {
    NAMES
} names_e;
#undef X

typedef struct {
    char *name;
} names_t;

#define X(_id, _name)    [_id] = { .name = _name },
static names_t const names[] = {
    NAMES
};
#undef X

int main(void) {
    int i;

    for (i=0; i < sizeof(names)/sizeof(names[0]); i++) {
        printf("%s\n", names[i].name);
    }

    printf("names[ABRAHAM] = %s\n", names[ABRAHAM].name);

#if defined(GEORGE)
    printf("names[GEORGE] = %s\n", names[GEORGE].name);
#endif

    return 0;
}

输出

John Adams
George Washington
Abraham Lincoln 
names[ABRAHAM] = Abraham Lincoln 

【问题讨论】:

  • 不,这是不可能的。 C 预处理器对数组一无所知。您没有在预处理器中定义任何名为 GEORGE 的东西,您只是使用该宏扩展为枚举和数组初始化,这些初始化将由编译器的后续阶段处理。
  • #if defined(GEORGE) 如果你是真的 #define GEORGE

标签: c c-preprocessor x-macros


【解决方案1】:

您可以尝试使用预处理器模式匹配器。这个概念只需要一个带有间接的SECOND 宏; GLUE 可以随意添加前缀:

#define GLUE(A,B) GLUE_I(A,B)
#define GLUE_I(A,B) A##B

#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(A,B,...) B

...对于仅包含“伪标识符”的 X 宏“列”:

#define NAMES \
X( JOHN, "John Adams" ) \
X( GEORGE, "George Washington" ) \
X( ABRAHAM, "Abraham Lincoln ")

...你可以这样做:

#define X(ID_, NAME_) SECOND(GLUE(SEARCH_FOR_,ID_),+0)
#define SEARCH_FOR_GEORGE ,+1
#if NAMES
printf("names[GEORGE] = %s\n", names[GEORGE].name);
#endif
#undef SEARCH_FOR_GEORGE
#define SEARCH_FOR_THOMAS ,+1
#if NAMES
#error Dewey wins!
#endif
#undef SEARCH_FOR_THOMAS
#undef X

这是有效的,因为SECOND 间接 扩展到第二个参数;所以SECOND(GLUE(SEARCH_FOR,ID_),+0)默认会扩展为+0;并且+0 的任何链都是有效的错误表达式。但是由于扩展是间接的,并且第一个参数是粘贴的标记,那么如果您将粘贴的标记SEARCH_FOR_GEORGE 定义为自身扩展,其中包含逗号,则逗号后面的表达式将成为新的第二个参数。 (问我是否需要在 Microsoft VS 预处理器上使用它;这需要稍作调整)。

【讨论】:

  • 谢谢。这完全解决了我的问题。
【解决方案2】:

不幸的是,您不能在宏主体内编写宏,因此使用已有的大纲来管理它会很棘手(我没有看到实现它的方法)。

您只能在预处理器中测试宏(而不是枚举元素的名称),并且不能通过预处理器编写宏(#define 无法根据其参数有效地生成新的#define) ,所以你必须做一些繁重的工作。

如果你定义

#define P_JOHN 0
#define P_GEORGE 1
#define P_ABRAHAM 2

那么你可以使用:

#define NAMES \
X( JOHN, "John Adams" ) \
X( GEORGE, "George Washington" ) \
X( ABRAHAM, "Abraham Lincoln ")

#define X(_id, _name)    _id = P_ ## _id,   // Crucial change
typedef enum
{
    NAMES
} names_e;
#undef X

typedef struct
{
    char *name;
} names_t;

#define X(_id, _name)    [_id] = { .name = _name },
static names_t const names[] =
{
    NAMES
};
#undef X

int main(void) {
    int i;

    for (i=0; i < sizeof(names)/sizeof(names[0]); i++) {
        printf("%s\n", names[i].name);
    }

    printf("names[ABRAHAM] = %s\n", names[ABRAHAM].name);

#if defined(P_GEORGE)        // Crucial change
    printf("names[GEORGE] = %s\n", names[GEORGE].name);
#endif

    return 0;
}

请注意,您有责任确保 P_* 名称具有唯一编号(而且我不确定您将如何处理 George H W Bush 和 George W Bush — 似乎存在冲突乔治华盛顿在那里)。

示例输出:

John Adams
George Washington
Abraham Lincoln 
names[ABRAHAM] = Abraham Lincoln 
names[GEORGE] = George Washington

【讨论】:

    猜你喜欢
    • 2012-08-05
    • 2022-10-21
    • 1970-01-01
    • 1970-01-01
    • 2011-07-02
    • 2011-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多