c++中通过宏模拟注解实现轻量反射:用meta_field宏在编译期生成constexpr元数据,类内声明后需手动调用register注册,运行时借助rtti和全局registry查字段元信息。

在C++中为类成员添加类似Java @Override或C# [DataMember]的自定义注解,并在运行时读取其元数据(如字段用途、序列化别名、校验规则),需要绕过语言原生限制构建轻量级反射映射系统。
定义宏驱动的Metadata注解语法
使用预处理器宏模拟注解声明,避免引入第三方IDL或代码生成器。宏展开后生成静态注册表条目。
在头文件中定义:#define META_FIELD(name, alias, required) static constexpr auto meta_##name = ::MetaField{#name, alias, required};
这一步不产生运行时开销,所有字符串字面量和布尔值在编译期确定,【宏名必须与成员变量名严格一致,否则解析时无法匹配】。
为类成员注入Metadata并触发自动注册
在类定义内部,紧贴成员变量下方插入宏调用:
struct User {
std::string name;
META_FIELD(name, "user_name", true)
int age;
META_FIELD(age, "user_age", false)
};
每个META_FIELD宏会声明一个constexpr静态变量,其类型为统一的MetaField结构体。这些变量虽未显式初始化,但因位于类作用域内,会被编译器纳入该类的符号上下文。
构建全局Metadata注册中心
创建单例Registry类,提供Add和Find接口:
template
registry_[typeid(T).name()][field_name] = meta;
}
在每个含META_FIELD的类定义后,添加显式注册语句:
User::Register("name", User::meta_name);
User::Register("age", User::meta_age);
这一步必须手动补全,【漏掉任一Register调用,对应字段的元数据将不可查】。
运行时解析指定类的全部Metadata
方法一:通过RTTI获取类型名→查Registry哈希表→遍历字段名列表
auto& fields = Registry::Instance().GetFields(typeid(User).name());
for (const auto& [field_name, meta] : fields) {
std::cout }
方法二:为类添加静态GetMetadata()成员函数,内联展开所有已注册字段,避免哈希查找开销:
static std::vector
return {meta_name, meta_age};
}
方法二性能更高,且编译期可验证字段完整性;方法一更灵活,支持跨模块动态注册。
将Metadata映射至实际内存偏移以实现通用序列化
第一步:使用offsetof计算每个成员相对于类起始地址的字节偏移
第二步:将MetaField结构体扩展为包含offset字段:
struct MetaField {
const char* name;
const char* alias;
bool required;
size_t offset;
};
第三步:在Register调用中传入offsetof结果:
User::Register("name", {User::meta_name.name, User::meta_name.alias, User::meta_name.required, offsetof(User, name)});
第四步:序列化时,根据MetaField.offset指针访问对应成员值:
const auto* user_ptr = reinterpret_cast
const auto* value_ptr = user_ptr + meta.offset;
这一步使Metadata真正具备操作能力——没有offset信息的元数据只能用于描述,无法驱动读写逻辑。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











