【__VA_ARGS__预编译问题】

项目场景:

在跨平台编译三方库过程中,头文件中涉及使用可变参数宏编译报错
__VA_ARGS__: 用于代指调用宏中的...项

由于项目代码的缘故,使用简化后的外部代码作为示例:


问题描述

使用g++编译时,头文件宏报错如下:

在这里插入图片描述

核心代码如下

//计数
#define ATTR(args) args
#define COUNT_PARMS_IMP(_1, _2, NUM, ...) NUM
#define COUNT_PARMS(...) \
ATTR(COUNT_PARMS_IMP(__VA_ARGS__, 2, 1, 0))

//根据可变参数的不同数目,执行对应的宏替换
//向缓存中写入数据
#define WR_ARG_1(arg) iSeArchive<<arg;
#define WR_ARG_2(arg, ...) WR_ARG_1(arg) ATTR(WR_ARG_1(__VA_ARGS__))
#define WR_ARGS(...) \
ATTR(SYMBOL_CATENATE_WITH_MACRO(WR_ARG_, ATTR(COUNT_PARMS(__VA_ARGS__)))(__VA_ARGS__))

......

#define SERIALIZE(PARENT, RESERVED, ...) \
    INTERNAL_SERIALIZATION_IMP(PARENT,RESERVED,__VA_ARGS__)
#define CSeArchive CSeArchive

//序列化
#define INTERNAL_SERIALIZATION_IMP(PARENT,RESERVED, ...)\
virtual std::string Serialize(){\
    CSeArchive iSeArchive;\
    std::string ret;\
if(#PARENT != #RESERVED)\
    {\
        std::string sParent = PARENT::Serialize();\
        iSeArchive.writeMemory(sParent.data(),sParent.size());\
    }\
    WR_ARGS(__VA_ARGS__);\
    iSeArchive.get_buf(ret);\
    return ret;}

示例代码
//demo.hpp
#include "Define.h"
struct Demo
{
    SERIALIZE(Demo,Demo) //编译报错
};

struct Demo1
{
	int num;
    SERIALIZE(Demo,Demo, num)
};


原因分析:

根据编译输出的报错信息 定位到预编译失败

g++ -E demo.hpp -I /pathToIncludes/ -o demo.txt
-E: 指定执行预编译处理
-I: 头文件包含路径
-o: 输出预编译

预编译后的代码存在语法错误,左/右移运算符后无左值
在这里插入图片描述


解决方案:

调整计算的方法,原计数方法对于__VA_ARGS__为空时,判断错误
COUNT_PARMS() --> COUNT_PARMS_IMP( , 2, 1, 0) , NUM对应第3项 -->1,即使为空依旧被视作占了一个参数,导致NUM计算错误

计数方法修改为:
若可变参数被忽略或为空时,"##" 操作将使预处理器去除前面的逗号。若不为空,可变参数则拼接到逗号后

//计数方法(支持__VA_ARGS__的size <=2 的情况)
#define ATTR(args) args
#define COUNT_PARMS_IMP(_0, _1, _2, NUM, ...) NUM
#define COUNT_PARMS(...) \
ATTR(COUNT_PARMS_IMP(0, ##__VA_ARGS__, 2, 1, 0))

对于计算可变参数为0时,不执行

#define WR_ARG_0(arg)

其余说明:

调用宏时,使用的参数超过所支持的计数,会导致形参替换原本用来计数的填充,导致拼接的用于执行操作的宏报错
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值