在现代软件开发中,反射是一种强大的特性,它可以支持程序在运行时查询和调用对象的属性和方法。
但是在C++中,没有内置的反射机制。我们可以通过一些巧妙的技术模拟反射的部分功能。
上一篇文章写了个简单的反射功能,这回完善一下,满足常见场景的使用吧。
我们这个简单反射库就称为refl吧。
refl
库是一个用C++编写的轻量级反射框架,它允许在编译时和运行时对对象的属性和方法进行查询和操作。
这个库需要使用C++17编译器编译。其完整源码如下:
#include <iostream>
#include <tuple>
#include <stdexcept>
#include <assert.h>
#include <string_view>
#include <optional>
#include <utility> // For std::forward
#include <unordered_map>
#include <functional>
#include <memory>
#include <any>
#include <type_traits> // For std::is_invocablenamespace refl {// 这个宏用于创建字段信息
#define REFLECTABLE_PROPERTIES(TypeName, ...) using CURRENT_TYPE_NAME = TypeName; \static constexpr auto properties() { return std::make_tuple(__VA_ARGS__); }
#define REFLECTABLE_MENBER_FUNCS(TypeName, ...) using CURRENT_TYPE_NAME = TypeName; \static constexpr auto member_funcs() { return std::make_tuple(__VA_ARGS__); }// 这个宏用于创建属性信息,并自动将字段名转换为字符串
#define REFLEC_PROPERTY(Name) refl::Property<decltype(&CURRENT_TYPE_NAME::Name), &CURRENT_TYPE_NAME::Name>(#Name)
#define REFLEC_FUNCTION(Func) refl::Function<decltype(&CURRENT_TYPE_NAME::Func), &CURRENT_TYPE_NAME::Func>(#Func)// 定义一个属性结构体,存储字段名称和值的指针template <typename T, T Value>struct Property {const char* name;constexpr Property(const char* name) : name(name) {}constexpr T get_value() const { return Value; }};template <typename T, T Value>struct Function {const char* name;constexpr Function(const char* name) : name(name) {}constexpr T get_func() const { return Value; }};// 使用 std::any 来处理不同类型的字段值和函数返回值template <typename T, typename Tuple, size_t N = 0>std::any __get_field_value_impl(T& obj, const char* name, const Tuple& tp) {if constexpr (N >= std::tuple_size_v<Tuple>) {return std::any();// Not Found!}else {const auto& prop = std::get<N>(tp);if (std::string_view(prop.name) == name) {return std::any(obj.*(prop.get_value()));}else {return __get_field_value_impl<T, Tuple, N + 1>(obj, name, tp);}}}// 使用 std::any 来处理不同类型的字段值和函数返回值template <typename T, size_t N = 0>std::any get_field_value(T& obj, const char* name) {return __get_field_value_impl(obj, name, T::properties());}// 使用 std::any 来处理不同类型的字段值和函数返回值template <typename T, typename Tuple, typename Value, size_t N = 0>std::any __assign_field_value_impl(T& obj, const char* name, const Value &value, const Tuple& tp) {if constexpr (N >= std::tuple_size_v<Tuple>) {return std::any();// Not Found!}else {const auto& prop = std::get<N>(tp);if (std::string_view(prop.name) == name) {if constexpr (std::is_assignable_v<decltype(obj.*(prop.get_value())), Value>) {obj.*(prop.get_value()) = value;return std::any(obj.*(prop.get_value()));}else {assert(false);// 无法赋值 类型不匹配!!return std::any();}}else {return __assign_field_value_impl<T, Tuple, Value, N + 1>(obj, name, value, tp);}}}template <typename T, typename Value>std::any assign_field_value(T& obj, const char* name, const Value& value) {return __assign_field_value_impl(obj, name, value, T::properties());}// 成员函数调用相关:template <bool check_arg_typs = true, typename T, typename FuncTuple, size_t N = 0, typename... Args>constexpr std::any __invoke_member_func_impl(T& obj, const char* name, const FuncTuple& tp, Args&&... args) {if constexpr (N >= std::tuple_size_v<FuncTuple>) {return std::any();// Not Found!}else {const auto& func = std::get<N>(tp);if (std::string_view(func.name) == name) {if constexpr (std::is_invocable_v<decltype(func.get_func()), T&, Args...>) {return std::invoke(func.get_func(), obj, std::forward<Args>(args)...);}else {assert(!check_arg_typs);// 调用参数不匹配return std::any();}}else {return __invoke_member_func_impl<check_arg_typs, T, FuncTuple, N + 1>(obj, name, tp, std::forward<Args>(args)...);}}}template <typename T, typename... Args>constexpr std::any invoke_member_func(T& obj, const char* name, Args&&... args) {constexpr auto funcs = T::member_funcs();return __invoke_member_func_impl(obj, name, funcs, std::forward<Args>(args)...);}template <typename T, typename... Args>constexpr std::any invoke_member_func_type_safe(T& obj, const char* name, Args&&... args) {constexpr auto funcs = T::member_funcs();return __invoke_member_func_impl<true>(obj, name, funcs, std::forward<Args>(args)...);}// 定义一个类型特征模板,用于获取属性信息template <typename T>struct For {static_assert(std::is_class_v<T>, "Reflector requires a class type.");// 遍历所有字段名称template <typename Func>static void for_each_propertie_name(Func&& func) {constexpr auto props = T::properties();std::apply([&](auto... x) {((func(x.name)), ...);}, props);}// 遍历所有字段值template <typename Func>static void for_each_propertie_value(T& obj, Func&& func) {constexpr auto props = T::properties();std::apply([&](auto... x) {((func(x.name, obj.*(x.get_value()))), ...);}, props);}// 遍历所有函数名称template <typename Func>static void for_each_member_func_name(Func&& func) {constexpr auto props = T::member_funcs();std::apply([&](auto... x) {((func(x.name)), ...);}, props);}};// ===============================================================// 以下是动态反射机制的支持代码:namespace dynamic {// 反射基类class IReflectable {public:virtual ~IReflectable() = default;virtual std::string_view get_type_name() const = 0;virtual std::any get_field_value_by_name(const char* name) const = 0;virtual std::any invoke_member_func_by_name(const char* name) = 0;virtual std::any invoke_member_func_by_name(const char* name, std::any param1) = 0;virtual std::any invoke_member_func_by_name(const char* name, std::any param1, std::any param2) = 0;virtual std::any invoke_member_func_by_name(const char* name, std::any param1, std::any param2, std::any param3) = 0;virtual std::any invoke_member_func_by_name(const char* name, std::any param1, std::any param2, std::any param3, std::any param4) = 0;// 不能无限增加,会增加虚表大小。最多支持4个参数的调用。};// 类型注册工具class TypeRegistry {public:using CreatorFunc = std::function<std::unique_ptr<IReflectable>()>;static TypeRegistry& instance() {static TypeRegistry registry;return registry;}void register_type(const std::string_view type_name, CreatorFunc creator) {creators[type_name] = std::move(creator);}std::unique_ptr<IReflectable> create(const std::string_view type_name) {if (auto it = creators.find(type_name); it != creators.end()) {return it->second();}return nullptr;}private:std::unordered_map<std::string_view, CreatorFunc> creators;};// 用于注册类型信息的宏
#define DECL_DYNAMIC_REFLECTABLE(TypeName) \friend class refl::dynamic::TypeRegistryEntry<TypeName>; \static std::string_view static_type_name() { return #TypeName; } \virtual std::string_view get_type_name() const override { return static_type_name(); } \static std::unique_ptr<::refl::dynamic::IReflectable> create_instance() { return std::make_unique<TypeName>(); } \static const bool is_registered; \std::any get_field_value_by_name(const char* name) const override { \return refl::get_field_value(*this, name); \} \std::any invoke_member_func_by_name(const char* name) override { \return refl::invoke_member_func(*static_cast<TypeName*>(this), name); \}\std::any invoke_member_func_by_name(const char* name, std::any param1) override { \return refl::invoke_member_func(*static_cast<TypeName*>(this), name, param1); \}\std::any invoke_member_func_by_name(const char* name, std::any param1, std::any param2) override { \return refl::invoke_member_func(*static_cast<TypeName*>(this), name, param1, param2); \}\std::any invoke_member_func_by_name(const char* name, std::any param1, std::any param2, std::any param3) override { \return refl::invoke_member_func(*static_cast<TypeName*>(this), name, param1, param2, param3); \}\std::any invoke_member_func_by_name(const char* name, std::any param1, std::any param2, std::any param3, std::any param4) override { \return refl::invoke_member_func(*static_cast<TypeName*>(this), name, param1, param2, param3, param4); \}\
// 用于在静态区域注册类型的辅助类template <typename T>class TypeRegistryEntry {public:TypeRegistryEntry() {::refl::dynamic::TypeRegistry::instance().register_type(T::static_type_name(), &T::create_instance);}};// 为每个类型定义注册变量,这段宏需要出现在cpp中。
#define REGEDIT_DYNAMIC_REFLECTABLE(TypeName) \const bool TypeName::is_registered = [] { \static ::refl::dynamic::TypeRegistryEntry<TypeName> entry; \return true; \}();}}// namespace refl
下面是对这个库的功能和使用方式的介绍:
静态反射
静态反射是在编译时进行的,refl
库通过宏和模板来实现:
REFLECTABLE_PROPERTIES
和REFLECTABLE_MENBER_FUNCS
宏用于声明类的属性和成员函数列表。这些宏在背后创建了一个包含属性或函数信息的元组。REFLEC_PROPERTY
和REFLEC_FUNCTION
宏用于将类的属性或函数与其名称关联起来,从而允许通过字符串来访问它们。
例如,我们可以定义一个MyStruct
类,并使用refl
库为其提供反射能力:
// 用户自定义的结构体
class MyStruct : public refl::dynamic::IReflectable {// 如果不需要动态反射,可以不从public refl::dynamic::IReflectable派生
public:int x{ 10 };double y{ 20.5f };int print() const {std::cout << "MyStruct::print called! " << "x: " << x << ", y: " << y << std::endl;return 666;}// 如果需要支持动态调用,参数必须是std::any,并且不能超过4个参数。int print_with_arg(std::any param) const {std::cout << "MyStruct::print called! " << " arg is: " << std::any_cast<int>(param) << std::endl;return 888;}REFLECTABLE_PROPERTIES(MyStruct,REFLEC_PROPERTY(x),REFLEC_PROPERTY(y));REFLECTABLE_MENBER_FUNCS(MyStruct,REFLEC_FUNCTION(print),REFLEC_FUNCTION(print_with_arg));DECL_DYNAMIC_REFLECTABLE(MyStruct)//动态反射的支持,如果不需要动态反射,可以去掉这行代码
};//动态反射注册类(不使用动态反射可以去除)
REGEDIT_DYNAMIC_REFLECTABLE(MyStruct)
动态反射
动态反射更加强大,无需包含被反射类型的头文件,支持在运行时通过字符串名称来访问和修改对象的属性,以及调用对象的方法。
IReflectable
是一个抽象基类,定义了一组用于动态反射的接口,比如get_field_by_name
和invoke_member_func_by_name
。TypeRegistry
是一个单例类,用于注册和创建反射类型的实例。
通过使用DECL_DYNAMIC_REFLECTABLE
和REGEDIT_DYNAMIC_REFLECTABLE
宏,开发者可以在类中声明并注册反射信息。
使用示例
以下是如何使用refl
库的示例:
int main() {MyStruct obj;// # 静态反射部分:// 打印所有字段名称refl::For<MyStruct>::for_each_propertie_name([](const char* name) {std::cout << "Field name: " << name << std::endl;});// 打印所有字段值refl::For<MyStruct>::for_each_propertie_value(obj, [](const char* name, auto&& value) {std::cout << "Field " << name << " has value: " << value << std::endl;});// 打印所有函数名称refl::For<MyStruct>::for_each_member_func_name([](const char* name) {std::cout << "Member func name: " << name << std::endl;});// 获取特定成员的值,如果找不到成员,则返回默认值auto x_value = refl::get_field_value(obj, "x");std::cout << "Field x has value: " << std::any_cast<int>(x_value) << std::endl;auto y_value = refl::get_field_value(obj, "y");std::cout << "Field y has value: " << std::any_cast<double>(y_value) << std::endl;//修改值:refl::assign_field_value(obj, "y", 33.33f);y_value = refl::get_field_value(obj, "y");std::cout << "Field y has modifyed,new value is: " << std::any_cast<double>(y_value) << std::endl;auto z_value = refl::get_field_value(obj, "z"); // "z" 不存在if (z_value.type().name() == std::string_view("int")) {std::cout << "Field z has value: " << std::any_cast<int>(z_value) << std::endl;}// 通过字符串调用成员函数 'print'auto print_ret = refl::invoke_member_func_type_safe(obj, "print");std::cout << "print member return: " << std::any_cast<int>(print_ret) << std::endl;std::cout << "---------------------" << std::endl;// 动态反射部分(动态反射完全不需要知道类型MyStruct的定义):// 动态创建 MyStruct 实例并调用方法auto instance = refl::dynamic::TypeRegistry::instance().create("MyStruct");if (instance) {std::cout << "Dynamic instance type: " << instance->get_type_name() << std::endl;// 这里可以调用 MyStruct 的成员方法auto x_value2 = instance->get_field_value_by_name("x");std::cout << "Field x has value: " << std::any_cast<int>(x_value2) << std::endl;instance->invoke_member_func_by_name("print");instance->invoke_member_func_by_name("print_with_arg", 10);instance->invoke_member_func_by_name("print_with_arg", 20, 222);//这个调用会失败,命中断言,因为print_with_arg只接受一个函数}return 0;
}
运行效果:
结论
尽管C++没有提供内置的反射机制,refl
库提供了一种简洁的方法来模拟这一功能。通过使用宏和模板,refl
库能够在编译时和运行时对对象的属性和方法进行操作,为C++程序带来了更多的灵活性和动态性。