C++17 实现反射机制
目录
C++17 反射机制完整实现
C++ 本身没有原生反射机制,但可以通过 C++17 的 constexpr、if constexpr、折叠表达式、std::tuple、std::string_view 等核心特性,实现一套类型安全、零运行时开销的静态反射系统,同时兼顾运行期的访问能力。
核心设计原理
- 编译期类型信息提取:利用编译器内置的函数签名宏,在编译期获取类型名、成员类型等信息,替代运行期 RTTI 的性能损耗
- 元数据固化:通过模板特化和宏,将类名、成员变量的名称、指针、类型信息等元数据,在编译期固化为 constexpr 常量
- 编译期遍历与访问:通过 std::apply + 折叠表达式实现元组的编译期遍历,配合 if constexpr 做类型分支判断,替代复杂的 SFINAE 写法
- 类型安全:所有类型检查均在编译期完成,类型不匹配直接编译报错,避免运行期异常
完整实现代码
1. 核心反射头文件 reflect.hpp
#pragma once
#include <string_view>
#include <tuple>
#include <type_traits>
#include <optional>
#include <stdexcept>
#include <utility>
// ==================== 编译期类型名获取(兼容GCC/Clang/MSVC) ====================
template <typename T>
constexpr std::string_view type_name() noexcept {
#if defined(__GNUC__) || defined(__clang__)
std::string_view name = __PRETTY_FUNCTION__;
constexpr std::string_view prefix = "constexpr std::string_view type_name() [with T = ";
constexpr std::string_view suffix = "]";
#elif defined(_MSC_VER)
std::string_view name = __FUNCSIG__;
constexpr std::string_view prefix = "constexpr std::string_view __cdecl type_name<";
constexpr std::string_view suffix = ">(void) noexcept";
#else
#error "Unsupported compiler, only GCC/Clang/MSVC are supported"
#endif
// 裁剪前后缀,提取干净的类型名
name.remove_prefix(prefix.size());
name.remove_suffix(suffix.size());
return name;
}
// ==================== 反射元数据结构定义 ====================
// 字段元数据:存储字段名、成员指针、类型信息
template <typename ClassType, typename FieldType>
struct FieldInfo {
std::string_view name;
FieldType ClassType::* ptr;
constexpr FieldInfo(std::string_view n, FieldType ClassType::* p) noexcept
: name(n), ptr(p) {}
using class_type = ClassType;
using field_type = FieldType;
};
// 成员函数元数据:存储函数名、函数指针、参数/返回值类型信息
template <typename ClassType, typename Ret, typename... Args>
struct MethodInfo {
std::string_view name;
Ret (ClassType::* ptr)(Args...);
constexpr MethodInfo(std::string_view n, Ret (ClassType::* p)(Args...)) noexcept
: name(n), ptr(p) {}
using class_type = ClassType;
using return_type = Ret;
using args_type = std::tuple<Args...>;
// 类型安全的函数调用
template <typename... CallArgs>
constexpr Ret invoke(ClassType& obj, CallArgs&&... args) const {
static_assert(std::is_invocable_r_v<Ret, decltype(ptr), ClassType&, CallArgs...>,
"Arguments type mismatch with method signature");
if constexpr (std::is_void_v<Ret>) {
(obj.*ptr)(std::forward<CallArgs>(args)...);
} else {
return (obj.*ptr)(std::forward<CallArgs>(args)...);
}
}
};
// ==================== 反射特性模板(核心存储) ====================
// 主模板,需要反射的类通过宏进行特化
template <typename T, typename = void>
struct ReflectTraits {
static constexpr bool is_reflectable = false;
};
// 类型是否可反射的编译期判断
template <typename T>
constexpr bool is_reflectable_v = ReflectTraits<T>::is_reflectable;
// 获取类名的编译期接口
template <typename T>
constexpr std::string_view class_name() noexcept {
static_assert(is_reflectable_v<T>, "Type is not reflectable!");
return ReflectTraits<T>::class_name;
}
// ==================== 核心遍历接口 ====================
// 编译期遍历所有字段元数据
template <typename T, typename Func>
constexpr void for_each_field_meta(Func&& func) {
static_assert(is_reflectable_v<T>, "Type is not reflectable!");
constexpr auto& fields = ReflectTraits<T>::fields;
std::apply([&func](auto&&... field) {
(func(std::forward<decltype(field)>(field)), ...);
}, fields);
}
// 运行期遍历对象的所有字段(名称、值、元数据)
template <typename T, typename Func>
void for_each_field(T& obj, Func&& func) {
for_each_field_meta<T>([&obj, &func](auto&& field) {
func(field.name, obj.*(field.ptr), field);
});
}
// const 版本遍历
template <typename T, typename Func>
void for_each_field(const T& obj, Func&& func) {
for_each_field_meta<T>([&obj, &func](auto&& field) {
func(field.name, obj.*(field.ptr), field);
});
}
// 编译期遍历所有成员函数元数据
template <typename T, typename Func>
constexpr void for_each_method_meta(Func&& func) {
static_assert(is_reflectable_v<T>, "Type is not reflectable!");
constexpr auto& methods = ReflectTraits<T>::methods;
std::apply([&func](auto&&... method) {
(func(std::forward<decltype(method)>(method)), ...);
}, methods);
}
// ==================== 按名访问接口 ====================
// 按名获取字段指针(类型安全,类型不匹配编译报错)
template <typename FieldType, typename T>
FieldType* get_field(T& obj, std::string_view name) noexcept {
static_assert(is_reflectable_v<T>, "Type is not reflectable!");
FieldType* ret = nullptr;
for_each_field_meta<T>([&](auto&& field) {
using F = typename std::decay_t<decltype(field)>::field_type;
if constexpr (std::is_same_v<F, FieldType>) {
if (field.name == name) {
ret = &(obj.*(field.ptr));
}
}
});
return ret;
}
// const 版本获取
template <typename FieldType, typename T>
const FieldType* get_field(const T& obj, std::string_view name) noexcept {
static_assert(is_reflectable_v<T>, "Type is not reflectable!");
const FieldType* ret = nullptr;
for_each_field_meta<T>([&](auto&& field) {
using F = typename std::decay_t<decltype(field)>::field_type;
if constexpr (std::is_same_v<F, FieldType>) {
if (field.name == name) {
ret = &(obj.*(field.ptr));
}
}
});
return ret;
}
// 按名设置字段值(自动类型检查,返回是否设置成功)
template <typename T, typename ValueType>
bool set_field(T& obj, std::string_view name, ValueType&& value) noexcept {
static_assert(is_reflectable_v<T>, "Type is not reflectable!");
bool success = false;
for_each_field_meta<T>([&](auto&& field) {
using F = typename std::decay_t<decltype(field)>::field_type;
if constexpr (std::is_assignable_v<F&, ValueType>) {
if (field.name == name) {
obj.*(field.ptr) = std::forward<ValueType>(value);
success = true;
}
}
});
return success;
}
// 按名调用成员函数(类型安全,参数不匹配编译报错)
template <typename Ret, typename T, typename... Args>
std::optional<Ret> invoke_method(T& obj, std::string_view name, Args&&... args) {
static_assert(is_reflectable_v<T>, "Type is not reflectable!");
std::optional<Ret> result;
for_each_method_meta<T>([&](auto&& method) {
using M = std::decay_t<decltype(method)>;
if constexpr (std::is_invocable_r_v<Ret, decltype(method.ptr), T&, Args...>) {
if (method.name == name) {
if constexpr (std::is_void_v<Ret>) {
method.invoke(obj, std::forward<Args>(args)...);
result.emplace();
} else {
result.emplace(method.invoke(obj, std::forward<Args>(args)...));
}
}
}
});
return result;
}
// ==================== 反射注册宏(极简侵入式) ====================
// 类内声明:必须放在类中,支持私有成员反射,声明友元权限
#define REFLECT_DECLARE(cls) \
friend struct ReflectTraits<cls>;
// 字段注册宏:类外使用,自动生成反射特性特化代码
#define REFLECT_FIELDS(cls, ...) \
template <> \
struct ReflectTraits<cls> { \
static constexpr bool is_reflectable = true; \
static constexpr std::string_view class_name = #cls; \
static constexpr auto fields = std::make_tuple( \
FieldInfo<cls, decltype(std::declval<cls>().*(&cls::__VA_ARGS__))>(#__VA_ARGS__, &cls::__VA_ARGS__)... \
); \
static constexpr auto methods = std::make_tuple(); \
};
// 字段+成员函数联合注册宏
#define REFLECT_FULL(cls, fields_args, methods_args) \
template <> \
struct ReflectTraits<cls> { \
static constexpr bool is_reflectable = true; \
static constexpr std::string_view class_name = #cls; \
static constexpr auto fields = std::make_tuple( \
FieldInfo<cls, decltype(std::declval<cls>().*(&cls::fields_args))>(#fields_args, &cls::fields_args)... \
); \
static constexpr auto methods = std::make_tuple( \
MethodInfo(#methods_args, &cls::methods_args)... \
); \
};2. 使用示例
#include "reflect.hpp"
#include <iostream>
#include <string>
// 测试类
class Person {
// 反射声明(必须放在类内,支持私有成员)
REFLECT_DECLARE(Person)
private:
std::string name;
int age;
public:
Person() = default;
Person(std::string n, int a) : name(std::move(n)), age(a) {}
// 测试成员函数
void say_hello() {
std::cout << "Hello, I'm " << name << ", " << age << " years old\n";
}
int get_age() const { return age; }
};
// 注册反射信息(类外使用)
REFLECT_FIELDS(Person, name, age)
int main() {
Person p("Zhang San", 25);
// ========== 1. 编译期获取类名 ==========
constexpr std::string_view cls_name = class_name<Person>();
std::cout << "Class name: " << cls_name << "\n\n";
// ========== 2. 遍历所有字段 ==========
std::cout << "=== Traverse all fields ===\n";
for_each_field(p, [](std::string_view name, auto& value, auto& field) {
std::cout << "Field name: " << name
<< ", Field type: " << type_name<typename decltype(field)::field_type>()
<< ", Value: " << value << "\n";
});
std::cout << "\n";
// ========== 3. 按名读写字段 ==========
std::cout << "=== Read/Write field by name ===\n";
// 读字段
if (auto* name_ptr = get_field<std::string>(p, "name")) {
std::cout << "Original name: " << *name_ptr << "\n";
}
// 写字段
bool set_success = set_field(p, "age", 30);
std::cout << "Set age success: " << std::boolalpha << set_success << "\n";
if (auto* age_ptr = get_field<int>(p, "age")) {
std::cout << "New age: " << *age_ptr << "\n";
}
std::cout << "\n";
// ========== 4. 实用场景:自动序列化 ==========
std::cout << "=== Auto serialize to JSON ===\n";
std::string json = "{";
bool first = true;
for_each_field(p, [&](std::string_view name, auto& value, auto&) {
if (!first) json += ", ";
first = false;
json += "\"" + std::string(name) + "\": ";
// 类型分支处理(编译期生成,无运行时开销)
if constexpr (std::is_same_v<std::decay_t<decltype(value)>, std::string>) {
json += "\"" + value + "\"";
} else if constexpr (std::is_arithmetic_v<std::decay_t<decltype(value)>>) {
json += std::to_string(value);
}
});
json += "}";
std::cout << json << "\n";
return 0;
}3. 编译与运行
编译时需指定 C++17 标准:
# GCC/Clang
g++ -std=c++17 main.cpp -o reflect_demo
./reflect_demo
# MSVC
cl /std:c++17 main.cpp /Fe:reflect_demo.exe
reflect_demo.exe运行输出:
Class name: Person
=== Traverse all fields ===
Field name: name, Field type: std::__cxx11::basic_string<char>, Value: Zhang San
Field name: age, Field type: int, Value: 25
=== Read/Write field by name ===
Original name: Zhang San
Set age success: true
New age: 30
=== Auto serialize to JSON ===
{"name": "Zhang San", "age": 30}核心特性说明
- 零运行时开销:所有元数据均为 constexpr 编译期常量,未使用的反射代码会被编译器优化掉,完全符合 C++ 零成本抽象原则
- 类型安全:字段读写、函数调用均在编译期做类型检查,类型不匹配直接编译报错,避免运行期类型转换异常
- 私有成员支持:通过类内的
REFLECT_DECLARE宏声明友元,完美支持私有成员的反射 - 编译器兼容:适配 GCC、Clang、MSVC 三大主流编译器,无平台依赖
- 极简语法:仅需 2 行宏即可完成类的反射注册,无侵入式代码修改
局限性与扩展方向
现有局限性
- 手动注册:C++17 无法自动提取类的成员列表,仍需手动通过宏注册成员(C++26 原生反射提案将解决此问题)
- 重载函数支持有限:当前实现对重载成员函数的支持较弱,需手动指定函数签名
- 继承关系不支持:无法自动继承父类的反射信息,需手动合并父类元数据
- 非标准特性依赖:类型名提取依赖编译器内置的
__PRETTY_FUNCTION__/__FUNCSIG__宏,非 C++ 标准语法,但主流编译器均支持
扩展方向
- 枚举反射:扩展 constexpr 枚举值与名称的映射,实现枚举的自动序列化
- 继承支持:通过模板递归合并父类与子类的反射元数据,实现继承体系的完整反射
- 构造函数反射:扩展元数据支持构造函数的注册与动态创建对象
- ORM/序列化框架:基于反射实现通用的 JSON/XML 序列化、数据库 ORM 映射
- 无宏方案:结合 Boost.PFR 库,实现聚合体的免宏反射(仅支持 POD/聚合类型)