预处理器由于 - '#'而失败后面没有宏参数

时间:2015-09-25 04:11:07

标签: c macros

我正在尝试简化数组的声明,但遇到了我正在使用的预处理器的问题。我的初始代码如下所示:

#define REQ_ENTRY(parm_1, parm_2)    \
#if defined(parm_1)                  \
    { parm_1, parm_2 },              \
#endif

typedef struct {
    int parm1,
        parm2;
} MyTypedef_t;

static const MyTypedef_t MyList[] =
{
    REQ_ENTRY( ID_1, 1 )
    REQ_ENTRY( ID_2, 2 )
    REQ_ENTRY( ID_3, 3 )
    REQ_ENTRY( ID_4, 4 )
    REQ_ENTRY( ID_5, 5 )
};

当然,构建失败了,错误消息为"错误:'#'后面没有宏参数"。这里解释了这个原因(Why compiler complain about this macro declaration

基本上,我试图避免将数组声明为如下,这确实有效:

static const MyTypedef_t MyList[] =
{
    #if defined (ID_1)
    { ID_1, 1 },
    #endif

    #if defined (ID_2)
    { ID_2, 2 },
    #endif

    #if defined (ID_3)
    { ID_3, 3 },
    #endif

    #if defined (ID_4)
    { ID_4, 4 },
    #endif

    #if defined (ID_5)
    { ID_5, 5 },
    #endif        
};

列表可能相当长,具体取决于项目的构建类型。我曾尝试过使用x-macros,但我想我会遇到同样的问题。我希望有人可能会看到一种方法来创建预处理器宏,以便我可以实现原始的糖语法?非常感谢任何见解。

3 个答案:

答案 0 :(得分:5)

没有很好的清洁解决方案。但是存在各种丑陋的解决方案。

如果您不介意在宏定义中同时包含 id 序列,可以这样解决:

#define CONCAT2(x,y) x##y
#define CONCAT(x,y) CONCAT2(x,y)
#define REQ_ENTRY_YES(p1, p2) { p1 , p2 }
#define REQ_ENTRY_NO(p1) 
#define IS_PAIR_HELPER(a, b, c, ...) c
#define IS_PAIR(...) IS_PAIR_HELPER(__VA_ARGS__, YES, NO)
#define REQ_ENTRY(pair) CONCAT(REQ_ENTRY_, IS_PAIR(pair))(pair)

#define ID_1 78723649, 1
#define ID_3 2347602, 3

typedef struct {
    int parm1,
        parm2;
} MyTypedef_t;

static const MyTypedef_t MyList[] =
{
    REQ_ENTRY( ID_1 )
    REQ_ENTRY( ID_2 )
    REQ_ENTRY( ID_3 )
    REQ_ENTRY( ID_4 )
    REQ_ENTRY( ID_5 )
};

使用-std=c11 -Wall -E运行gcc,并仅显示MyList定义:

static const MyTypedef_t MyList[] =
{
    { 78723649 , 1 }

    { 2347602 , 3 }


};

你可以在#define ID_x宏中使用任何第二个值来做同样的事情,只要有一个;真实参数可以添加到REQ_ENTRY。但这需要一些额外的麻烦。

答案 1 :(得分:3)

遗憾的是,defined运算符仅在#if#ifelse的上下文中可用,但不适用于宏扩展。就目前而言,我同意rici关于不同丑陋的解决方案。

这是一个解决方案,要求定义的值被括号括起来。然后,您可以将ID用作常规值,也可以将其传递给DEF,当宏在括号中时,它会扩展为1或者如果不是则扩展为0。 (这是我学会了here的一招。)

借助DEF宏,您可以创建扩展或忽略给定定义的辅助宏:

/* Auxiliary macros */

#define M_CHECK(...) M_CHECK_(__VA_ARGS__)
#define M_CHECK_(a, b, ...) b

#define M_IS_PAREN(x) M_CHECK(M_IS_PAREN_ x, 0)
#define M_IS_PAREN_(...) 1, 1

#define M_CONCAT(a, b) M_CONCAT_(a, b)
#define M_CONCAT_(a, b) a ## b

/* Conditional definition macros */

#define DEF(x) M_IS_PAREN(x)

#define DEF_IF_0(id, def)
#define DEF_IF_1(id, def) {id, def},

#define COND_DEF(x, y) M_CONCAT(DEF_IF_, DEF(x))(x, y)

/* Implementation */

#define ID_1 (27)
#define ID_3 (28)
#define ID_4 (29)

static const MyTypedef_t MyList[] = {
    COND_DEF(ID_1, 1)
    COND_DEF(ID_2, 2)
    COND_DEF(ID_3, 3)
    COND_DEF(ID_4, 4)
    COND_DEF(ID_5, 5)
};

这将产生:

static const MyTypedef_t MyList[] = {
    {(27), 1},

    {(28), 3},
    {(29), 4},

};

您还可以在代码中使用DEF宏,该代码将扩展为0或1:

printf("ID_1 is %s.\n", DEF(ID_1) ? "defined" : "undefined");

答案 2 :(得分:1)

这是我在不引入冗余的情况下能够得到的最接近的结果:

#define PREPROCESSOR_IF #if
#define PREPROCESSOR_ENDIF #endif
#define PREPROCESSOR_NEWLINE /*
*/

#define REQ_ENTRY(parm_1, parm_2)                         \
PREPROCESSOR_IF defined(parm_1)      PREPROCESSOR_NEWLINE \
    { parm_1, parm_2 },              PREPROCESSOR_NEWLINE \
PREPROCESSOR_ENDIF

typedef struct {
    int parm1,
        parm2;
} MyTypedef_t;

static const MyTypedef_t MyList[] =
{
    REQ_ENTRY( ID_1, 1 )
    REQ_ENTRY( ID_2, 2 )
    REQ_ENTRY( ID_3, 3 )
    REQ_ENTRY( ID_4, 4 )
    REQ_ENTRY( ID_5, 5 )
};

您只需要使用 -E-CC 运行一次预处理器,然后编译第一次预处理器传递的结果,但是由于(例如)它不起作用

#if defined (ID_1) /*
*/ { ID_1, 1 }, /*
*/ #endif

不被预处理器识别为不同的行,因为注释仅被一个空格替换。

我能够想出一个使用冗余的解决方案,但它并不比你提出的更好(每次都写出整个语句):

#define PREPROCESSOR_IF #if
#define PREPROCESSOR_ENDIF #endif
#define PREPROCESSOR_DEFINE #define
#define PREPROCESSOR_NEWLINE /*
*/

#define REQ_ENTRY_1(parm_1, parm_2) PREPROCESSOR_IF defined(parm_1)
#define REQ_ENTRY_2(parm_1, parm_2) { parm_1, parm_2 }, 
#define REQ_ENTRY_3(parm_1, parm_2) PREPROCESSOR_ENDIF

PREPROCESSOR_DEFINE ID_1 (27)
PREPROCESSOR_DEFINE ID_3 (28)
PREPROCESSOR_DEFINE ID_4 (29)

typedef struct {
    int parm1,
        parm2;
} MyTypedef_t;

static const MyTypedef_t MyList[] =
{
    REQ_ENTRY_1( ID_1, 1 )
    REQ_ENTRY_2( ID_1, 1 )
    REQ_ENTRY_3( ID_1, 1 )

    REQ_ENTRY_1( ID_2, 2 )
    REQ_ENTRY_2( ID_2, 2 )
    REQ_ENTRY_3( ID_2, 2 )

    REQ_ENTRY_1( ID_3, 3 )
    REQ_ENTRY_2( ID_3, 3 )
    REQ_ENTRY_3( ID_3, 3 )

    REQ_ENTRY_1( ID_4, 4 )
    REQ_ENTRY_2( ID_4, 4 )
    REQ_ENTRY_3( ID_4, 4 )

    REQ_ENTRY_1( ID_5, 5 )
    REQ_ENTRY_2( ID_5, 5 )
    REQ_ENTRY_3( ID_5, 5 )
};

这确实使用上述两步编译过程按需要编译。