项目场景:
在跨平台编译三方库过程中,头文件中涉及使用可变参数宏编译报错
__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)
其余说明:
调用宏时,使用的参数超过所支持的计数,会导致形参替换原本用来计数的填充,导致拼接的用于执行操作的宏报错