背景
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,有兴趣可以看看。