预处理器失败,因为 - '#' 后面没有跟宏参数
Preprocessor fails due to - '#' is not followed by a macro parameter
我正在尝试简化数组的声明,但是 运行 我使用的预处理器出现了问题。我的初始代码如下所示:
#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 )
};
当然,构建失败,错误消息为"error: '#' is not followed by a macro parameter"。此处解释了其原因 (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,但我想我也会遇到同样的问题。我希望有人能看到一种创建预处理器宏的方法,这样我就可以实现原始的糖语法?非常感谢任何见解。
没有很好的干净解决方案。但是有不同丑陋的解决方案。
如果不介意在宏定义中同时包含id和sequence,可以这样解决:
#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 )
};
运行 通过 gcc 和 -std=c11 -Wall -E
,并且仅显示 MyList
定义:
static const MyTypedef_t MyList[] =
{
{ 78723649 , 1 }
{ 2347602 , 3 }
};
您可以通过在 #define ID_x
宏中使用 any 第二个值来做同样的事情,只要有一个;真正的参数可以添加到REQ_ENTRY
。但这需要一些额外的技巧。
遗憾的是,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");
这是我在不引入冗余的情况下能够得到的最接近结果:
#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
未被识别为预处理器的不同行,因为注释仅被一个替换 space。
我能够想出一个使用冗余的解决方案,但它并不比你提出的更好(每次都写出整个语句):
#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 )
};
这确实使用上述两步编译过程按需要编译。
我正在尝试简化数组的声明,但是 运行 我使用的预处理器出现了问题。我的初始代码如下所示:
#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 )
};
当然,构建失败,错误消息为"error: '#' is not followed by a macro parameter"。此处解释了其原因 (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,但我想我也会遇到同样的问题。我希望有人能看到一种创建预处理器宏的方法,这样我就可以实现原始的糖语法?非常感谢任何见解。
没有很好的干净解决方案。但是有不同丑陋的解决方案。
如果不介意在宏定义中同时包含id和sequence,可以这样解决:
#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 )
};
运行 通过 gcc 和 -std=c11 -Wall -E
,并且仅显示 MyList
定义:
static const MyTypedef_t MyList[] =
{
{ 78723649 , 1 }
{ 2347602 , 3 }
};
您可以通过在 #define ID_x
宏中使用 any 第二个值来做同样的事情,只要有一个;真正的参数可以添加到REQ_ENTRY
。但这需要一些额外的技巧。
遗憾的是,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");
这是我在不引入冗余的情况下能够得到的最接近结果:
#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
未被识别为预处理器的不同行,因为注释仅被一个替换 space。
我能够想出一个使用冗余的解决方案,但它并不比你提出的更好(每次都写出整个语句):
#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 )
};
这确实使用上述两步编译过程按需要编译。