Cereal序列化库中宏递归展开的优化方案及技术解析
未优化:参考nlohmann json设计Cereal宏 一行声明序列化函数
宏实现
#include <cereal/cereal.hpp>// 强制二次展开
#define CEREAL_EXPAND( x ) x// 获取宏参数的数量,对应的CEREAL_PASTEn宏NAME
#define CEREAL_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, \_16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, \_29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, \_42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, \_55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME// 参数拼接宏(支持1-64个参数)
#define CEREAL_PASTE(...) CEREAL_EXPAND(CEREAL_GET_MACRO(__VA_ARGS__, \CEREAL_PASTE64, CEREAL_PASTE63, CEREAL_PASTE62, CEREAL_PASTE61, \CEREAL_PASTE60, CEREAL_PASTE59, CEREAL_PASTE58, CEREAL_PASTE57, \CEREAL_PASTE56, CEREAL_PASTE55, CEREAL_PASTE54, CEREAL_PASTE53, \CEREAL_PASTE52, CEREAL_PASTE51, CEREAL_PASTE50, CEREAL_PASTE49, \CEREAL_PASTE48, CEREAL_PASTE47, CEREAL_PASTE46, CEREAL_PASTE45, \CEREAL_PASTE44, CEREAL_PASTE43, CEREAL_PASTE42, CEREAL_PASTE41, \CEREAL_PASTE40, CEREAL_PASTE39, CEREAL_PASTE38, CEREAL_PASTE37, \CEREAL_PASTE36, CEREAL_PASTE35, CEREAL_PASTE34, CEREAL_PASTE33, \CEREAL_PASTE32, CEREAL_PASTE31, CEREAL_PASTE30, CEREAL_PASTE29, \CEREAL_PASTE28, CEREAL_PASTE27, CEREAL_PASTE26, CEREAL_PASTE25, \CEREAL_PASTE24, CEREAL_PASTE23, CEREAL_PASTE22, CEREAL_PASTE21, \CEREAL_PASTE20, CEREAL_PASTE19, CEREAL_PASTE18, CEREAL_PASTE17, \CEREAL_PASTE16, CEREAL_PASTE15, CEREAL_PASTE14, CEREAL_PASTE13, \CEREAL_PASTE12, CEREAL_PASTE11, CEREAL_PASTE10, CEREAL_PASTE9, \CEREAL_PASTE8, CEREAL_PASTE7, CEREAL_PASTE6, CEREAL_PASTE5, \CEREAL_PASTE4, CEREAL_PASTE3, CEREAL_PASTE2, CEREAL_PASTE1)(__VA_ARGS__))// 定义PASTE宏(1-64个参数)
#define CEREAL_PASTE2(func, param) func(param)
#define CEREAL_PASTE3(func, param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE2(func, __VA_ARGS__))
#define CEREAL_PASTE4(func, param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE3(func, __VA_ARGS__))
#define CEREAL_PASTE5(func, param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE4(func, __VA_ARGS__))
#define CEREAL_PASTE6(func, param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE5(func, __VA_ARGS__))
#define CEREAL_PASTE7(func, param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE6(func, __VA_ARGS__))
#define CEREAL_PASTE8(func, param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE7(func, __VA_ARGS__))
#define CEREAL_PASTE9(func, param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE8(func, __VA_ARGS__))
#define CEREAL_PASTE10(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE9(func, __VA_ARGS__))
#define CEREAL_PASTE11(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE10(func, __VA_ARGS__))
#define CEREAL_PASTE12(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE11(func, __VA_ARGS__))
#define CEREAL_PASTE13(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE12(func, __VA_ARGS__))
#define CEREAL_PASTE14(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE13(func, __VA_ARGS__))
#define CEREAL_PASTE15(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE14(func, __VA_ARGS__))
#define CEREAL_PASTE16(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE15(func, __VA_ARGS__))
#define CEREAL_PASTE17(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE16(func, __VA_ARGS__))
#define CEREAL_PASTE18(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE17(func, __VA_ARGS__))
#define CEREAL_PASTE19(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE18(func, __VA_ARGS__))
#define CEREAL_PASTE20(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE19(func, __VA_ARGS__))
#define CEREAL_PASTE21(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE20(func, __VA_ARGS__))
#define CEREAL_PASTE22(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE21(func, __VA_ARGS__))
#define CEREAL_PASTE23(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE22(func, __VA_ARGS__))
#define CEREAL_PASTE24(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE23(func, __VA_ARGS__))
#define CEREAL_PASTE25(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE24(func, __VA_ARGS__))
#define CEREAL_PASTE26(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE25(func, __VA_ARGS__))
#define CEREAL_PASTE27(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE26(func, __VA_ARGS__))
#define CEREAL_PASTE28(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE27(func, __VA_ARGS__))
#define CEREAL_PASTE29(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE28(func, __VA_ARGS__))
#define CEREAL_PASTE30(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE29(func, __VA_ARGS__))
#define CEREAL_PASTE31(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE30(func, __VA_ARGS__))
#define CEREAL_PASTE32(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE31(func, __VA_ARGS__))
#define CEREAL_PASTE33(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE32(func, __VA_ARGS__))
#define CEREAL_PASTE34(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE33(func, __VA_ARGS__))
#define CEREAL_PASTE35(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE34(func, __VA_ARGS__))
#define CEREAL_PASTE36(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE35(func, __VA_ARGS__))
#define CEREAL_PASTE37(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE36(func, __VA_ARGS__))
#define CEREAL_PASTE38(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE37(func, __VA_ARGS__))
#define CEREAL_PASTE39(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE38(func, __VA_ARGS__))
#define CEREAL_PASTE40(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE39(func, __VA_ARGS__))
#define CEREAL_PASTE41(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE40(func, __VA_ARGS__))
#define CEREAL_PASTE42(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE41(func, __VA_ARGS__))
#define CEREAL_PASTE43(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE42(func, __VA_ARGS__))
#define CEREAL_PASTE44(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE43(func, __VA_ARGS__))
#define CEREAL_PASTE45(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE44(func, __VA_ARGS__))
#define CEREAL_PASTE46(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE45(func, __VA_ARGS__))
#define CEREAL_PASTE47(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE46(func, __VA_ARGS__))
#define CEREAL_PASTE48(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE47(func, __VA_ARGS__))
#define CEREAL_PASTE49(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE48(func, __VA_ARGS__))
#define CEREAL_PASTE50(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE49(func, __VA_ARGS__))
#define CEREAL_PASTE51(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE50(func, __VA_ARGS__))
#define CEREAL_PASTE52(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE51(func, __VA_ARGS__))
#define CEREAL_PASTE53(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE52(func, __VA_ARGS__))
#define CEREAL_PASTE54(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE53(func, __VA_ARGS__))
#define CEREAL_PASTE55(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE54(func, __VA_ARGS__))
#define CEREAL_PASTE56(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE55(func, __VA_ARGS__))
#define CEREAL_PASTE57(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE56(func, __VA_ARGS__))
#define CEREAL_PASTE58(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE57(func, __VA_ARGS__))
#define CEREAL_PASTE59(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE58(func, __VA_ARGS__))
#define CEREAL_PASTE60(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE59(func, __VA_ARGS__))
#define CEREAL_PASTE61(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE60(func, __VA_ARGS__))
#define CEREAL_PASTE62(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE61(func, __VA_ARGS__))
#define CEREAL_PASTE63(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE62(func, __VA_ARGS__))
#define CEREAL_PASTE64(func,param, ...) func(param), CEREAL_EXPAND(CEREAL_PASTE63(func, __VA_ARGS__))// 成员处理宏
#define CEREAL_MEMBER_NVP_NON_INTRUSIVE(member) ::cereal::make_nvp(#member, t.member)// 主序列化宏:非侵入的
#define CEREAL_SERIALIZE_NON_INTRUSIVE(Type, ...) \
template <class Archive> \
void serialize(Archive& ar, const Type& t) { \ar( CEREAL_PASTE(CEREAL_MEMBER_NVP_NON_INTRUSIVE, __VA_ARGS__) ); \
}\
template <class Archive> \
void serialize(Archive& ar, Type& t) { \ar( CEREAL_PASTE(CEREAL_MEMBER_NVP_NON_INTRUSIVE, __VA_ARGS__) ); \
}// 主序列化宏:侵入的
#define CEREAL_SERIALIZE_INTRUSIVE(Type, ...) \
template <class Archive> \
void serialize(Archive& ar) { \ar( CEREAL_PASTE(CEREAL_NVP, __VA_ARGS__) ); \
}
技术解析:
🔧 优化后的宏定义
#define CEREAL_EXPAND(x) x // 强制二次展开
#define CEREAL_PASTE2(func, x) func(x)
#define CEREAL_PASTE3(func, x, ...) func(x), CEREAL_EXPAND(CEREAL_PASTE2(func, __VA_ARGS__))
#define CEREAL_PASTE4(func, x, ...) func(x), CEREAL_EXPAND(CEREAL_PASTE3(func, __VA_ARGS__))
#define CEREAL_PASTE5(func, x, ...) func(x), CEREAL_EXPAND(CEREAL_PASTE4(func, __VA_ARGS__))
🔁 方案1:CEREAL_EXPAND
的作用
1. 强制二次展开
#define CEREAL_PASTE3(func, x, ...) func(x), CEREAL_EXPAND(CEREAL_PASTE2(func, __VA_ARGS__))
-
展开流程(以
CEREAL_PASTE3(F, a, b, c)
为例):- 首次展开:
F(a), CEREAL_EXPAND(CEREAL_PASTE2(F, b, c))
CEREAL_EXPAND
强制展开其参数:CEREAL_PASTE2(F, b, c) → F(b), F(c)
- 最终结果:
F(a), F(b), F(c)
- 首次展开:
2. 必要性分析
-
无
CEREAL_EXPAND
时:
CEREAL_PASTE2(func, __VA_ARGS__)
作为文本保留,后续不再展开。 -
有
CEREAL_EXPAND
时:
通过CEREAL_EXPAND
包裹,预处理器会立即展开内层宏,确保递归链继续。
⚠️ 方案2缺陷(无CEREAL_EXPAND
)
1.没有二次展开
// 原方案(错误示例)
#define CEREAL_PASTE3(func, x, ...) func(x), CEREAL_PASTE2(func, __VA_ARGS__)
- 问题:当调用
CEREAL_PASTE3(func, x, y, z)
时:- 首次展开:
func(x), CEREAL_PASTE2(func, y, z)
- 由于预处理器不会自动展开嵌套宏,
CEREAL_PASTE2(func, y, z)
被视为整体而非宏调用。 - 结果:
func(x), CEREAL_PASTE2(func, y, z)
→ 后续参数未展开,导致编译错误。
- 首次展开:
2. 关键机制:宏展开的惰性
- 预处理器展开规则:
- 当宏参数包含其他宏(如
__VA_ARGS__
中的CEREAL_PASTE2
)时,不会立即展开,而是作为文本保留。 - 只有当前层宏完全展开后,才会处理嵌套宏(若未被
#
或##
修饰)。
- 当宏参数包含其他宏(如
📊 技术方案对比
场景 | 方案1(含CEREAL_EXPAND ) | 方案2(无CEREAL_EXPAND ) |
---|---|---|
CEREAL_PASTE3(F, a, b) | F(a), F(b) | F(a), CEREAL_PASTE2(F, b) |
CEREAL_PASTE4(F, a,b,c) | F(a), F(b), F(c) | F(a), CEREAL_PASTE3(F, b,c) |
嵌套宏展开 | 递归展开至最内层 | 停止于第一层 |
🧠 技术原理:预处理器展开规则
- 递归展开条件:
- 宏必须完全独立(未被
#
/##
修饰)。 - 每轮扫描只展开当前层的宏标识符,嵌套宏需等待外层展开完成。
- 宏必须完全独立(未被
- CEREAL_EXPAND的突破性:
- 通过中间层宏(
CEREAL_EXPAND
)隔离,使内层宏(如CEREAL_PASTE2
)满足“独立标识符”条件,触发其展开。
- 通过中间层宏(
💻 实际应用示例
1. 序列化宏实现
// 成员处理宏
#define CEREAL_MEMBER_NVP_NON_INTRUSIVE(member) ::cereal::make_nvp(#member, t.member)// 主序列化宏:非侵入的
#define CEREAL_SERIALIZE_NON_INTRUSIVE(Type, ...) \
template <class Archive> \
void serialize(Archive& ar, const Type& t) { \ar( CEREAL_PASTE(CEREAL_MEMBER_NVP_NON_INTRUSIVE, __VA_ARGS__) ); \
}// 主序列化宏:侵入的
#define CEREAL_SERIALIZE_INTRUSIVE(...) \
template <class Archive> \
void serialize(Archive& ar) { \ar( CEREAL_PASTE(CEREAL_NVP, __VA_ARGS__) ); \
}
2. 展开过程演示
struct Point
{float x, y, z;CEREAL_SERIALIZE_INTRUSIVE(x, y, z)
};
调用:CEREAL_SERIALIZE_INTRUSIVE(Point, x, y, z)
逐步展开:
// 步骤1: 替换为PASTE4宏
ar( CEREAL_PASTE4(CEREAL_NVP, x, y, z) );// 步骤2: 展开PASTE4
ar( CEREAL_NVP(x), CEREAL_EXPAND(CEREAL_PASTE3(CEREAL_NVP, y, z)) );// 步骤3: 展开内层EXPAND → 触发PASTE3
ar( ::cereal::make_nvp("x", t.x), CEREAL_PASTE3(CEREAL_NVP, y, z) );// 步骤4: 继续展开PASTE3
ar( ::cereal::make_nvp("x", t.x), ::cereal::make_nvp("y", t.y), ::cereal::make_nvp("z", t.z) );
⚠️ 注意事项
-
递归深度限制:
- 预处理器递归层数通常为64层(编译器相关),超限需拆分参数或减少嵌套。
- 更多层需要开启编译警告忽略。
-
兼容性:
__VA_ARGS__
为C++11特性,需确保编译器支持。
-
调试技巧:
- 使用
gcc -E
查看预处理结果,验证展开逻辑:
g++ -E -P test.cpp | grep -A 10 "serialize"
- 使用
💎 总结
通过引入CEREAL_EXPAND
强制展开嵌套宏,解决了原方案中递归链断裂的问题。其核心原理是绕过预处理器的惰性展开机制,使内层宏作为独立标识符被处理。此方案在保持代码简洁性的同时,确保了任意数量参数的递归展开可靠性,为Cereal等库的宏封装提供了健壮基础。