C语言的预处理器无法先展开宏再拼接符号?可以!

背景

最近接到一个需求,要实现一个脚本,能提取.h文件里定义的所有全局变量的值,这些全局变量都是结构体变量,名字是结构体类型名场景后缀——每个.h对应的场景都是唯一的,所以.h内所有变量名的后缀一致。

我的解决方案

我想到的解决方案是,写一个打桩的.c文件,里面实现一个accessor函数,根据参数结构体名场景名返回对应的变量值。结构体名种类固定,可以写死,但场景名无限多,必须动态传入,于是想到使用gcc的-D选项。该选项可以实现#define语句的效果,即定义一个,场景就可以通过宏展开来传递了,示例代码如下:

int luma_cinema = 3;
int luma_theater = 4;

#define PASTE(param_name, scene_name) param_name ## _ ## scene_name

int main() {
    //return luma_##SCENE; //符号拼接只能在#define语句内出现!
    return PASTE(luma, SCENE);
}

脚本通过如下命令行传递场景名:

gcc -DSCENE=cinema test_sharp.c

我的方案失败了

上面的代码编译不过:

test_sharp.c: In function ‘main’:
test_sharp.c:8:18: error: ‘luma_SCENE’ undeclared (first use in this function); did you mean ‘SCENE’?
     return PASTE(luma, SCENE);
                  ^
test_sharp.c:4:39: note: in definition of macro ‘PASTE’
 #define PASTE(param_name, scene_name) param_name ## _ ## scene_name
                                       ^~~~~~~~~~
test_sharp.c:8:18: note: each undeclared identifier is reported only once for each function it appears in
     return PASTE(luma, SCENE);
                  ^
test_sharp.c:4:39: note: in definition of macro ‘PASTE’
 #define PASTE(param_name, scene_name) param_name ## _ ## scene_name
                                       ^~~~~~~~~~

分析

查看cpp命令(C preprocessor)的输出

查看cpp的输出

cpp -DSCENE=cinema test_sharp.c

发现只执行了符号拼接,没有执行宏展开

# 1 "test_sharp.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test_sharp.c"
int luma_cinema = 3;
int luma_theater = 4;



int main() {

    return luma_SCENE;
}

自己的理解

当展开PASTE宏时,cpp只会执行一次展开,即宏函数的展开,不会再对宏函数的参数进行展开了。

要想先展开宏定义,再展开宏拼接,需要两次展开,怎么实现两次展开呢?

查看cpp的官方文档

查看官方文档,发现这行话:
宏先展开再拼接
看样子就是定义2个宏函数,类似于二级指针的意思。

解决问题

修改我的原始代码:

int luma_cinema = 3;
int luma_theater = 4;

#define _PASTE(param_name, scene_name) param_name ## _ ## scene_name
#define PASTE(param_name, scene_name) _PASTE(param_name, scene_name)

int main() {
    //return luma_##SCENE;
    return PASTE(luma, SCENE);
}

这次gcc编译通过,运行效果:

$ gcc -DSCENE=cinema test_sharp.c
$ ./a.out
$ echo $?
3

a.out程序退出时返回3,说明符合代码预期。

查看cpp的输出,也符合预期:

# 1 "test_sharp.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test_sharp.c"
int luma_cinema = 3;
int luma_theater = 4;




int main() {

    return luma_cinema;
}

后记

昨天这个问题逼得我无奈使用sed动态替换打桩.c里的场景名,非常笨拙,并熬夜看了一部分GNU m4的资料。没想到预处理器本身就支持,以后有时间要好好研究预处理器。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值