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等庫的宏封裝提供了健壯基礎。