ShineEngine 反射系统技能清单
描述
ShineEngine 反射系统提供运行时类型自省能力,支持字段访问、方法调用、容器反射等功能。通过编译期绑定技术,实现零开销的字段访问。
使用场景
- •编辑器属性面板自动生成
- •脚本与 C++ 互操作
- •序列化/反序列化
- •运行时类型检查
1. 类型注册
1.1 基础结构体注册
cpp
#include "EngineCore/reflection/Reflection.h"
struct Vec3 {
float x, y, z;
Vec3() : x(0), y(0), z(0) {}
Vec3(float x, float y, float z) : x(x), y(y), z(z) {}
};
// 注册到反射系统
REFLECT_STRUCT(Vec3)
.field<&Vec3::x>("x")
.field<&Vec3::y>("y")
.field<&Vec3::z>("z")
;
1.2 完整类型注册(带元数据)
cpp
struct Player {
std::string name;
int hp;
int mp;
bool isAlive;
Vec3 position;
std::vector<std::string> skills;
std::map<std::string, int> stats;
void TakeDamage(int dmg) { hp -= dmg; }
void Heal(int amount) { hp += amount; }
};
REFLECT_STRUCT(Player)
.field<&Player::name>("name").DisplayName("Player Name")
.field<&Player::hp>("hp").Range(0, 1000)
.field<&Player::mp>("mp").Range(0, 500)
.field<&Player::isAlive>("isAlive").DisplayName("Alive")
.field<&Player::position>("position")
.field<&Player::skills>("skills")
.field<&Player::stats>("stats")
.method<&Player::TakeDamage>("TakeDamage").ScriptCallable()
.method<&Player::Heal>("Heal").ScriptCallable()
;
2. 字段访问
2.1 反射方式(有约 20x 开销)
cpp
// 获取类型信息
auto* typeInfo = shine::reflection::TypeRegistry::Get().FindFast(
shine::reflection::GetTypeId<Player>()
);
// 查找字段
auto* nameField = typeInfo->FindField("name");
auto* hpField = typeInfo->FindField("hp");
// 读取字段
Player p{"Hero", 100, 50, true};
std::string nameValue;
int hpValue;
nameField->Get(&p, &nameValue);
hpField->Get(&p, &hpValue);
// 写入字段
nameField->Set(&p, &std::string("NewHero"));
hpField->Set(&p, &200);
2.2 编译期绑定(零开销!)
cpp
// 使用 CT_GET/CT_SET 进行编译期绑定的字段访问
Player p{"Hero", 100, 50, true};
// 读取 - 完全内联,零开销
auto name = shine::reflection::CT_GET<&Player::name>(p);
auto hp = shine::reflection::CT_GET<&Player::hp>(p);
// 写入 - 完全内联,零开销
shine::reflection::CT_SET<&Player::name>(p, "NewHero");
shine::reflection::CT_SET<&Player::hp>(p, 200);
// 编译期偏移获取
constexpr size_t hpOffset = shine::reflection::CT_OFFSETOF<&Player::hp>();
性能对比:
- •反射 Get: ~0.25 ns (20x 开销)
- •CT_GET: ~0.001 ns (与原生相同!)
3. 方法调用
3.1 反射调用
cpp
auto* typeInfo = shine::reflection::TypeRegistry::Get().FindFast(
shine::reflection::GetTypeId<Player>()
);
auto* method = typeInfo->FindMethod("TakeDamage");
Player p{"Hero", 100, 50, true};
int damage = 25;
method->Invoke(&p, &damage, nullptr);
3.2 编译期方法绑定(推荐)
使用 DSL 宏注册方法时,自动使用 FastMethodCall 模板特化:
cpp
// 内部实现 - 模板特化,零间接调用
template<auto MethodPtr>
inline void FastMethodCall(void* inst, void** args, void* ret) {
using Traits = MethodTraits<decltype(MethodPtr)>;
auto* obj = static_cast<typename Traits::ClassType*>(inst);
// ... 模板特化调用
}
性能: 方法调用开销 ~3x
4. 容器反射
4.1 支持的容器类型
| 容器类型 | 支持状态 |
|---|---|
| std::vector<T> | ✅ 完全支持 |
| std::map<K,V> | ✅ 完全支持 |
| std::set<T> | ✅ 完全支持 |
4.2 容器操作
cpp
// 通过 offset 直接访问容器
auto* typeInfo = shine::reflection::TypeRegistry::Get().FindFast(
shine::reflection::GetTypeId<Player>()
);
auto* skillsField = typeInfo->FindField("skills");
auto* statsField = typeInfo->FindField("stats");
// 获取容器指针
auto* skills = reinterpret_cast<std::vector<std::string>*>(
(char*)&player + skillsField->offset
);
auto* stats = reinterpret_cast<std::map<std::string, int>*>(
(char*)&player + statsField->offset
);
// 操作容器
skills->push_back("Fireball");
stats->at("strength") = 100;
性能: 容器反射开销 ~1x (无额外开销)
5. 类型信息查询
5.1 获取类型信息
cpp
// 通过类型获取 ID
constexpr uint32_t playerId = shine::reflection::GetTypeId<Player>();
// 通过字符串获取类型
auto* typeInfo = shine::reflection::TypeRegistry::Get().Find("Player");
// 快速查找(使用缓存)
auto* typeInfoFast = shine::reflection::TypeRegistry::Get().FindFast(playerId);
5.2 查询字段和方法
cpp
// 查找字段
auto* field = typeInfo->FindField("hp");
if (field) {
fmt::print("Field: {}, TypeID: {}, Size: {}\n",
field->name, field->typeId, field->size);
}
// 遍历所有字段
for (const auto& f : typeInfo->fields) {
fmt::print("Field: {}, Offset: {}, IsPOD: {}\n",
f.name, f.offset, f.isPod);
}
// 查找方法
auto* method = typeInfo->FindMethod("TakeDamage");
if (method) {
fmt::print("Method: {}, Params: {}\n",
method->name, method->paramTypes.size());
}
6. 元数据系统
6.1 添加元数据
cpp
REFLECT_STRUCT(Player)
.field<&Player::hp>("hp")
.Meta("Min", 0)
.Meta("Max", 1000)
.Meta("DisplayName", "Health Points")
.Range(0, 1000)
.field<&Player::name>("name")
.DisplayName("Player Name")
.Meta("Category", "Basic")
;
6.2 读取元数据
cpp
auto* field = typeInfo->FindField("hp");
if (auto* minVal = field->GetMeta(MetaKeys::Min)) {
if (auto* p = std::get_if<float>(minVal)) {
fmt::print("Min value: {}\n", *p);
}
}
7. 编译期功能
7.1 编译期类型名获取
cpp
// 获取类型名的字符串字面量 constexpr auto typeName = shine::reflection::GetTypeName<Player>(); // typeName = "Player" // 编译期哈希 constexpr uint32_t typeId = shine::reflection::GetTypeId<Player>();
7.2 编译期成员名获取
cpp
// 通过成员指针获取字段名 constexpr auto fieldName = shine::reflection::member_name<&Player::hp>; // fieldName = "hp" // 编译期偏移计算 constexpr size_t hpOffset = offsetof(Player, hp); // 或使用 CT_OFFSETOF constexpr size_t hpOffset2 = shine::reflection::CT_OFFSETOF<&Player::hp>();
8. 性能优化
8.1 性能基准
| 操作 | 性能 | 开销 |
|---|---|---|
| 字段查找 | 4.5M ops/sec | - |
| 原生字段访问 | 0.012 ns | 1.0x |
| 反射字段访问 | 0.25 ns | ~20x |
| CT_GET 访问 | 0.001 ns | 0.1x ✅ |
| 容器操作 | ~1x | 无开销 |
| 方法调用 | ~3x | - |
8.2 优化建议
- •高频访问使用 CT_GET/CT_SET: 对于游戏循环中的字段访问,使用编译期绑定
- •使用 FindFast: 类型查找使用缓存版本
- •缓存字段指针: 避免重复 FindField 调用
cpp
// 优化前
for (int i = 0; i < 1000; ++i) {
auto* field = type->FindField("hp"); // 每次查找
field->Get(&obj, &value);
}
// 优化后
auto* hpField = type->FindField("hp"); // 缓存指针
for (int i = 0; i < 1000; ++i) {
hpField->Get(&obj, &value);
}
// 极致优化 - 使用 CT_GET
for (int i = 0; i < 1000; ++i) {
auto hp = shine::reflection::CT_GET<&Player::hp>(player); // 零开销
}
9. API 参考
9.1 核心类型
| 类型 | 说明 |
|---|---|
TypeId | uint32_t,类型唯一标识 |
TypeInfo | 类型元信息,包含字段和方法 |
FieldInfo | 字段元信息 |
MethodInfo | 方法元信息 |
9.2 核心函数
| 函数 | 说明 |
|---|---|
GetTypeId<T>() | 获取类型的编译期 ID |
GetTypeName<T>() | 获取类型名的字符串字面量 |
Hash(str) | FNV-1a 字符串哈希 |
CT_GET<&T::field>(obj) | 编译期字段读取 |
CT_SET<&T::field>(obj, val) | 编译期字段写入 |
CT_OFFSETOF<&T::field>() | 编译期偏移计算 |
9.3 FieldInfo 方法
| 方法 | 说明 |
|---|---|
Get(inst, out) | 读取字段值 |
Set(inst, in) | 写入字段值 |
FindField(name) | 查找字段 |
GetMeta(key) | 获取元数据 |
10. 构建与验证
编译要求:
- •C++23 标准
- •MSVC 19.50+
验证命令:
bash
# 构建测试 Build.bat module EngineCore # 运行反射测试 .\exe\ReflectionTestd.exe
测试输出示例:
code
============================================================
ShineEngine 反射系统测试套件
============================================================
字段查找: 4.48 M ops/sec
Getter: 0.2385 ns (开销: 19.3x)
Setter: 0.2382 ns (开销: 19.3x)
方法调用: 3.5x 开销
类型查找: 1.0x 加速 (FindFast)
============================================================
测试完成
============================================================