目录
1.简介
UDRefl (Ubpa Dynamic Reflection)是一个基于C++20 的高性能动态反射库,由 Ubpa 开发并开源(MIT 许可证)。它提供了在运行时灵活访问和操作 C++ 类型结构的能力,无需依赖 RTTI(运行时类型信息)或异常处理机制,采用无污染头文件设计,显著提升编译效率。UDRefl 的核心优势在于极致性能和强大功能,同时保持简洁易用的 API,特别适合需要动态类型操作的场景,如插件系统、序列化 / 反序列化、脚本绑定等。
核心特征:
- 极致性能:采用编译期模板元编程和高效数据结构,反射操作几乎无额外运行时开销
- 完全动态:无需 RTTI 和异常,纯标准 C++20 实现,不依赖外部工具或编译器扩展
- 零头文件污染:反射信息在.cpp 文件中生成,修改数据时最小化编译时间
- 全面类型支持 :
- 单继承、多继承和虚拟继承
- 构造函数、析构函数、赋值运算符
- 虚函数、纯虚函数、重载函数、任意参数数量的方法
- 全局域、方法和枚举
- 指针、引用、数组、标准容器、迭代器
- 高级功能 :
- 方法调用时自动参数类型转换
- 可在任意类层次调用方法
- 为所有反射对象添加额外属性
- 无需类型声明即可操作自定义类型(适合插件系统)
- 元函数支持(operator+、operator-、begin/end 等)
- 自举能力(将 UDRefl 接口注册到自身中)
2.核心概念
| 概念 | 描述 |
|---|---|
| ReflMngr | 库的核心管理器,负责注册和管理所有类型信息、域、方法和属性,提供主要接口 |
| TypeInfo | 类型信息结构体,包含类型大小、对齐、多态性、域信息、方法信息、基类信息和属性集 |
| ObjectView | 对象视图,包含对象指针 (void*) 和类型 (Type),不管理资源,类似 std::string_view |
| SharedObject | 对象视图的子类,多了 std::shared_ptr<void>来管理对象生命周期 |
| Field/FieldPtr | 域表示类型的成员对象,FieldPtr 用于获取变量指针,支持 5 种类型(基本、虚拟、静态、动态共享、动态缓冲) |
| Method/MethodPtr | 方法表示类型的成员函数,MethodPtr 用于调用成员函数,支持变量方法、常量方法和静态方法 |
| Attr (属性) | 附加信息,是一个共享对象,可附加到类型、域或方法上,按 Type 索引 |
| Name | 字符视图 (std::string_view)+ 哈希值,用于高效查找类型、域和方法 |
| Type | 类型名 (std::string_view)+ 哈希值,支持类型计算(如添加 const、引用等修饰) |
核心API:
| 功能 | API 用法 |
|---|---|
| 获取反射管理器 | ReflMngr::Instance() |
| 注册类型 | mngr.RegisterType<T>() |
| 注册成员变量 | mngr.AddField<&T::成员>("名称") |
| 注册成员方法 | mngr.AddMethod<&T::方法>("名称") |
| 动态创建对象 | mngr.MakeShared(Type_of<T>) |
| 访问变量 | obj.Var("变量名") |
| 调用方法 | obj.Invoke("方法名", 参数...) |
| 类型转换 | obj.Var("x").As<int>() |
| 遍历成员 | FieldRange_of<T>() |
3.安装与集成
1.下载源码
git clone https://github.com/Ubpa/UDRefl.git
2.CMake 编译生成 VS 工程
- 打开 x64 Native Tools Command Prompt for VS 2022(VS 自带命令行)
- 进入 UDRefl 源码根目录,执行命令:
cpp
# 创建编译文件夹
mkdir build
cd build
# 生成 VS2022 工程(64位)
cmake .. -G "Visual Studio 17 2022" -A x64 -DCMAKE_CXX_STANDARD=20
3.VS 编译库文件
- 进入
build文件夹,打开UDRefl.sln - 切换模式:Debug / Release(建议都编译一次)
- 右键解决方案 → 生成解决方案
- 编译成功后,会生成:
- 头文件:
源码根目录/include - 库文件:
build/lib/Debug/UDRefl.lib/build/lib/Release/UDRefl.lib
- 头文件:
4.使用详解
4.1.注册流程
UDRefl 的使用主要分为注册 和使用两个步骤:
1.注册类型 :使用ReflMngr::RegisterType<T>()模板函数,自动获取类型名、大小和对齐
cpp
ReflMngr::RegisterType<Vec>();
2.注册域 :使用AddField模板函数,支持自动识别域类型
cpp
Mngr.AddField<&Vec::x>("x");
Mngr.AddField<&Vec::y>("y");
Mngr.AddField<&Vec::y_static>(Type_of<Vec>, "y_static");
3.注册方法 :使用AddMethod模板函数,支持重载函数(通过MemFuncOf解决)
cpp
Mngr.AddMethod<&Vec::norm>("norm");
Mngr.AddMethod<MemFuncOf<Funcs, void()>>::get(&Funcs::bar)>("bar");
Mngr.AddMethod<&Funcs::cat>(Type_of<Funcs>, "cat");
4.注册基类 :使用AddBases模板函数,自动处理继承关系(包括虚继承)
cpp
Mngr.AddBases<D, B, C>();
5.注册属性 :在注册类型、域或方法时附加属性,或使用AddTypeAttr/AddFieldAttr/AddMethodAttr接口
4.2.使用流程
1.创建对象 :使用MakeShared创建共享对象
cpp
SharedObject v = Mngr.MakeShared(Type_of<Vec>);
2.访问变量 :使用Var方法获取变量,支持直接赋值和读取
cpp
v.Var("x") = 3;
v.Var("y") = 4;
std::cout << "x: " << v.Var("x") << std::endl;
3.调用方法 :使用Invoke方法调用对象方法,支持自动参数转换
cpp
std::cout << "norm: " << v.Invoke("norm") << std::endl;
4.遍历成员 :使用FieldRange_of和MethodRange_of遍历类型的域和方法
cpp
for (auto&& [name, info] : FieldRange_of<Vec>)
std::cout << name.GetView() << std::endl;
5.元函数操作:支持运算符重载和容器接口,简化代码
cpp
// 运算符
SharedObject sum = v1 + v2;
// 容器遍历
for(auto elem : v_container) { /*...*/ }
6.类型运算:支持添加 const、引用、指针等类型修饰
cpp
ObjectView const_v = v.AddConst();
ObjectView ref_v = v.AddLValueReference();
4.3.完整示例
我们创建一个 Person 类,演示类型注册、成员访问、方法调用、遍历成员,这是 UDRefl 最核心的用法。
cpp
#include <iostream>
#include <string>
// 必须包含的头文件
#include <UDRefl/UDRefl.h>
// 使用命名空间(简化代码)
using namespace Ubpa::UDRefl;
using namespace std;
// ===================== 1. 定义普通类(无任何反射宏,零污染)=====================
struct Person {
string name; // 成员变量
int age; // 成员变量
// 成员方法
void SayHello() {
cout << "Hello! 我是" << name << ",今年" << age << "岁" << endl;
}
// 带参数的方法
void SetAge(int a) {
age = a;
}
};
// ===================== 2. 注册类型(必须在cpp中注册,头文件无侵入)=====================
void RegisterPerson() {
auto& mngr = ReflMngr::Instance(); // 获取反射管理器单例
// 1. 注册 Person 类型
mngr.RegisterType<Person>();
// 2. 注册成员变量
mngr.AddField<&Person::name>("name");
mngr.AddField<&Person::age>("age");
// 3. 注册成员方法
mngr.AddMethod<&Person::SayHello>("SayHello");
mngr.AddMethod<&Person::SetAge>("SetAge");
}
// ===================== 3. 使用反射操作对象 =====================
int main() {
// 第一步:注册类型(程序初始化时执行一次)
RegisterPerson();
auto& mngr = ReflMngr::Instance();
// ===================== 基础用法 =====================
// 1. 动态创建 Person 对象(智能指针,自动管理内存)
SharedObject p = mngr.MakeShared(Type_of<Person>);
// 2. 动态赋值成员变量
p.Var("name") = "小明";
p.Var("age") = 20;
// 3. 动态获取成员变量
cout << "姓名:" << p.Var("name").As<string>() << endl;
cout << "年龄:" << p.Var("age").As<int>() << endl;
// 4. 动态调用成员方法
p.Invoke("SayHello"); // 无参方法
p.Invoke("SetAge", 25); // 带参方法
p.Invoke("SayHello");
// ===================== 高级用法:遍历成员 =====================
cout << "\n=== 遍历 Person 所有成员变量 ===" << endl;
for (auto&& [name, field] : FieldRange_of<Person>) {
cout << "成员名:" << name.ToString() << endl;
}
return 0;
}
输出:
cpp
姓名:小明
年龄:20
Hello! 我是小明,今年20岁
Hello! 我是小明,今年25岁
=== 遍历 Person 所有成员变量 ===
成员名:name
成员名:age
5.应用场景
- 插件系统:无需类型声明即可操作插件中的自定义类型,实现动态扩展
- 序列化 / 反序列化:自动遍历对象成员,实现高效的数据序列化和反序列化
- 脚本绑定:将 C++ 类和方法注册到脚本语言(如 Lua),通过反射实现脚本与 C++ 的交互
- 动态 UI 生成:根据对象类型信息自动生成属性编辑器,适用于游戏编辑器等工具
- 依赖注入:通过反射自动创建对象并注入依赖,实现松耦合架构
- 单元测试:通过反射自动调用测试方法,生成测试报告
6.与其他反射库对比
| 特性 | UDRefl | RTTR | Boost.Reflect |
|---|---|---|---|
| C++ 标准 | C++20 | C++11+ | C++11+ |
| 依赖 | 无(纯标准 C++) | 无 | Boost 库 |
| RTTI / 异常 | 不需要 | 需要 | 需要 |
| 性能 | 极高(编译期优化) | 高 | 中 |
| 头文件污染 | 无(反射信息在.cpp) | 有 | 有 |
| 多继承 / 虚继承 | 支持 | 支持 | 有限支持 |
| 元函数支持 | 完整 | 部分 | 无 |
| 自举能力 | 支持 | 不支持 | 不支持 |
7.注意事项
- C++20 要求:必须使用支持 C++20 的编译器,如 GCC 10+、Clang 10 + 或 MSVC 2019 16.8.5+
- 资源管理 :
ObjectView不管理资源,需确保指向的对象生命周期长于视图本身 - 多线程安全 :
ReflMngr的注册操作应在单线程初始化阶段完成,运行时访问是线程安全的 - 动态域:动态域分为共享和缓冲两种类型,缓冲域只能存储简单类型
- 方法调用:调用虚函数时会自动解析正确的函数地址,支持多态调用
8.总结
UDRefl 是 C++20 生态中一款极具价值的动态反射库,它以极致性能、零依赖和强大功能为核心优势,为 C++ 开发者提供了运行时类型操作的高效解决方案。其简洁的 API 设计和完善的文档支持,降低了反射技术的使用门槛,特别适合需要动态类型操作的中大型项目。无论是开发插件系统、序列化框架还是脚本绑定层,UDRefl 都能显著提升开发效率和代码质量。