C++ 无反射 JSON 序列化:AIGCJson 的宏与模板魔法
C++ 缺少原生反射,要在运行时拿到成员变量的名称和类型几乎不可能,因此实现 JSON 序列化免不了大量样板代码。AIGCJson 的做法很取巧:靠预处理器宏和模板元编程,在编译期生成所有元数据和转换函数,让用户只写一行宏就能注册字段。
下面拆开看看它是怎么做到的。
怎么做到的?三步
AIGCJson 的套路可以总结成三步:
- 宏:利用
#__VA_ARGS__把成员变量列表原样变成字符串字面量,从而在运行时拿到字段名。 - 变长参数模板:把宏后面的参数包展开,在编译期逐一绑定到字段引用上。
- SFINAE:编译期判断类型,对基础类型、容器和自定义结构体走不同的处理分支。
AIGC_JSON_HELPER 宏
整个库的入口就是这个宏:
#define AIGC_JSON_HELPER(...) \
std::map<std::string, std::string> __aigcDefaultValues; \
bool AIGCJsonToObject(aigc::JsonHelperPrivate &handle, rapidjson::Value &jsonValue, std::vector<std::string>&names) { \
std::vector<std::string> standardNames = handle.GetMembersNames(#__VA_ARGS__); \
if (names.size() <= standardNames.size()) { \
for (int i = names.size(); i < (int)standardNames.size(); i++) { \
names.push_back(standardNames[i]); \
} \
} \
return handle.SetMembers(names, 0, jsonValue, __aigcDefaultValues, __VA_ARGS__); \
}
在类里写 AIGC_JSON_HELPER(name, age) 时,编译器会在该类中插入 AIGCJsonToObject 和 AIGCObjectToJson 两个成员函数,一个负责反序列化,一个负责序列化。
#__VA_ARGS__ 是预处理器字符串化操作符,它会把 name, age 直接变成字符串 "name, age",也就是说,字段名不是通过反射推断出来的,就是把源代码里的文本直接拿过去了。
字段名提取
拿到 "name, age" 这个字符串之后,JsonHelperPrivate::GetMembersNames 做的工作很简单:按逗号分割,去掉空格和引号,返回一个 vector<string> {"name", "age"},这就是 JSON 的 Key 了。
std::vector<std::string> GetMembersNames(const std::string membersStr) {
std::vector<std::string> array = StringSplit(membersStr);
StringTrim(array);
return array;
}

