__VA_OPT__(,)能否检测末尾空逗号?如何构造宏避免双逗号错误?
背景
C语言允许初始化器中使用末尾逗号,如下两种写法等价:
int foo[] = { 10, 20, 30 }; int baz[] = { 10, 20, 30, };
基于此构造初始化宏也可正常工作:
#define bar(...) { __VA_ARGS__ } int foo[] = bar(10, 20, 30); int baz[] = bar(10, 20, 30,);
若要在参数列表后添加40(即使列表为空),如下宏写法在正常参数下可正常工作:
#define bar(...) { __VA_ARGS__ __VA_OPT__(,) 40 } int foo[] = bar(10, 20, 30); int baz[] = bar();
问题
但当传入带末尾空逗号的参数时:
#define bar(...) { __VA_ARGS__ __VA_OPT__(,) 40 } int foo[] = bar(10, 20, 30, );
预处理器会生成双逗号{ 10, 20, 30,, 40 },导致编译报错且无法直接定位问题逗号:
r.c: In function ‘main’: r.c:160:43: error: expected expression before ‘,’ token 160 | #define bar(...) { __VA_ARGS__ __VA_OPT__(,) 40 } | ^ r.c:164:21: note: in expansion of macro ‘bar’ 164 | int foo[] = bar(10, 20, 30,); | ^~~
复杂示例
在递归树形结构体初始化场景中,此类错误更难定位:
struct menu { char *name; struct menu **submenu; }; #define MENU_LIST(...) (struct menu*[]){ __VA_ARGS__ __VA_OPT__(,) NULL } #define MENU_ITEM(...) &(struct menu){ __VA_ARGS__ }
如下代码中因末尾多余逗号导致报错,却难以快速定位:
r.c:11:65: error: expected expression before ‘,’ token 11 | #define MENU_LIST(...) (struct menu*[]){ __VA_ARGS__ __VA_OPT__(,) NULL } | ^ r.c:113:25: note: in expansion of macro ‘MENU_LIST’ 113 | struct menu *mymenu[] = MENU_LIST( | ^~~~~~~~~ r.c:122:9: note: in expansion of macro ‘MENU_ITEM’ 122 | MENU_ITEM( | ^~~~~~~~~ r.c:124:36: note: in expansion of macro ‘MENU_LIST’ 124 | .submenu = MENU_LIST( | ^~~~~~
对应的错误代码:
struct menu *mymenu[] = MENU_LIST( // line 113 MENU_ITEM( .name = "planets", .submenu = MENU_LIST( MENU_ITEM( .name = "Earth" ), MENU_ITEM( .name = "Mars" ), MENU_ITEM( .name = "Jupiter" ) ) ), MENU_ITEM( // line 122 .name = "stars", .submenu = MENU_LIST( // line 124 MENU_ITEM( .name = "Sun" ), MENU_ITEM( .name = "Vega" ), MENU_ITEM( .name = "Proxima Centauri" ), ) ), MENU_ITEM( .name = "satellites", .submenu = MENU_LIST( MENU_ITEM( .name = "ISS" ), MENU_ITEM( .name = "OreSat0" ) ) ) );
技术问询
__VA_OPT__(,)能否检测到末尾无后续内容的逗号?- 能否构造宏避免双逗号生成,或让编译器产生更易定位的错误?
(注:此问题非XY问题,核心需求为解决宏的双逗号场景)
内容的提问来源于stack exchange,提问作者KJ7LNW
相关产品推荐
相关产品推荐

