AgentSkillsCN

reflection-skill

ShineEngine 反射系统完整指南,包含类型注册、字段访问、方法调用、编译期优化和性能测试

中文原作
SKILL.md
--- frontmatter
name: reflection-skill
description: ShineEngine 反射系统完整指南,包含类型注册、字段访问、方法调用、编译期优化和性能测试

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 ns1.0x
反射字段访问0.25 ns~20x
CT_GET 访问0.001 ns0.1x
容器操作~1x无开销
方法调用~3x-

8.2 优化建议

  1. 高频访问使用 CT_GET/CT_SET: 对于游戏循环中的字段访问,使用编译期绑定
  2. 使用 FindFast: 类型查找使用缓存版本
  3. 缓存字段指针: 避免重复 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 核心类型

类型说明
TypeIduint32_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)
============================================================
                    测试完成
============================================================