背景

C++ 一直缺少反射(Reflection),虽然 C++20 引入了 std::reflect,但目前编译器支持还很有限。

实际项目中,我们经常需要:

  • 根据字符串创建对象
  • 自动注入依赖
  • 序列化/反序列化

这篇文章聊聊怎么在 C++ 里实现一套轻量反射系统。

反射的核心:类型注册

反射的本质是在运行时动态查询类型信息。实现思路很简单——用全局注册表。

1. 基础类型注册表

#include <functional>
#include <unordered_map>
#include <string>
#include <memory>

class TypeRegistry {
public:
    template<typename T>
    static void registerType(const std::string& name) {
        creators()[name] = []() -> std::any {
            return std::make_any<T>();
        };
    }

    static std::any create(const std::string& name) {
        auto it = creators().find(name);
        if (it != creators().end()) {
            return it->second();
        }
        throw std::runtime_error("Unknown type: " + name);
    }

private:
    static auto& creators() {
        static std::unordered_map<std::string, std::function<std::any()>> map;
        return map;
    }
};

2. 宏简化注册

#define REGISTER_TYPE(T) \
    namespace { \
        struct Registrar##T { \
            Registrar##T() { \
                TypeRegistry::registerType<T>(#T); \
            } \
        }; \
        static Registrar##T registrar_##T; \
    }

3. 使用

struct User {
    std::string name;
    int age;
};

REGISTER_TYPE(User)

int main() {
    auto any_user = TypeRegistry::create("User");
    auto& user = std::any_cast<User&>(any_user);
    user.name = "BvBeJ";
    user.age = 28;
}

进阶:带构造函数参数

上面的实现只能调用默认构造函数。实际场景往往需要传参:

class TypeFactory {
public:
    template<typename Base, typename Derived, typename... Args>
    static void registerType(const std::string& name) {
        creators()[name] = [](Args... args) -> std::any {
            return std::any(std::make_shared<Derived>(args...));
        };
    }

    template<typename Base, typename... Args>
    std::shared_ptr<Base> create(const std::string& name, Args... args) {
        auto it = creators().find(name);
        if (it != creators().end()) {
            auto any = it->second(args...);
            return std::any_cast<std::shared_ptr<Base>>(any);
        }
        throw std::runtime_error("Unknown type: " + name);
    }

private:
    static auto& creators() {
        static std::unordered_map<std::string, std::function<std::any(Args...)>> map;
        return map;
    }
};

实用:依赖注入容器

基于反射系统,可以实现一个简单的 DI 容器:

template<typename T>
class Container {
public:
    template<typename... Args>
    std::shared_ptr<T> resolve(Args... args) {
        return std::make_shared<T>(args...);
    }
};

// 特化:已经是 shared_ptr
template<typename T>
class Container<std::shared_ptr<T>> {
public:
    std::shared_ptr<T> resolve() {
        return get<T>();
    }

    template<typename U>
    void registerService() {
        services()[typeid(U)] = std::make_shared<U>();
    }

private:
    template<typename U>
    std::shared_ptr<U> get() {
        auto it = services().find(typeid(U));
        if (it != services().end()) {
            return std::any_cast<std::shared_ptr<U>>(it->second);
        }
        throw std::runtime_error("Service not registered");
    }

    static auto& services() {
        static std::unordered_map<std::type_index, std::any> map;
        return map;
    }
};

应用场景

1. Plugin 系统

// 插件接口
class IPlugin {
public:
    virtual ~IPlugin() = default;
    virtual void init() = 0;
    virtual std::string name() const = 0;
};

// 插件注册
REGISTER_TYPE(MyPlugin)

// 动态加载
auto plugin = factory.create<IPlugin>("MyPlugin");
plugin->init();

2. 序列化框架

class Serializer {
public:
    template<typename T>
    void registerType() {
        type_handler<T> = [](const T& obj, JSON& json) {
            // 使用反射遍历成员
            reflect_members(obj, json);
        };
    }

    template<typename T>
    void to_json(const T& obj, JSON& json) {
        type_handler<T>(obj, json);
    }

private:
    std::unordered_map<std::type_index, std::function<void(std::any, JSON&)>> type_handler;
};

现代替代:C++20 Reflection

C++20 引入了 <reflect> 库(部分编译器支持):

#include <reflect>

constexp auto r = reflexpr(MyStruct);
using fields = get_members_t<r>;
// ...

但目前 GCC/Clang/MSVC 支持还不完整,线上项目暂时还是需要自己实现。

总结

方法优点缺点
手写注册表简单、完全可控需要手动维护
宏自动注册简洁宏的调试困难
C++20 Reflection标准、语言级支持编译器支持有限

自己造轮子的过程很有意思,也能更深入理解语言的本质。


这个项目的完整代码放在了 GitHub,有兴趣可以看看。