深入探讨C++的高级反射机制(2):写个能用的反射库

在现代软件开发中,反射是一种强大的特性,它可以支持程序在运行时查询和调用对象的属性和方法。
但是在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_invocable

namespace 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_PROPERTIESREFLECTABLE_MENBER_FUNCS宏用于声明类的属性和成员函数列表。这些宏在背后创建了一个包含属性或函数信息的元组。
  • REFLEC_PROPERTYREFLEC_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_nameinvoke_member_func_by_name
  • TypeRegistry是一个单例类,用于注册和创建反射类型的实例。

通过使用DECL_DYNAMIC_REFLECTABLEREGEDIT_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++程序带来了更多的灵活性和动态性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/746727.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

最新版Git安装指南使用指南

首先&#xff0c;访问Git的官方网站https://git-scm.com下载适用于您操作系统的安装包。您也可以选择使用阿里云镜像来加速下载过程。 也可以用国内地址下载https://pan.quark.cn/s/0293d76e58bchttps://pan.quark.cn/s/0293d76e58bc安装过程 在这里插入图片描述 2、点击“…

前端 CSS 经典:backface-visibility 属性

前言&#xff1a;backface-visibility 属性可以使反转 180deg 的元素隐藏&#xff0c;使用这个属性实现卡片翻转效果 效果 代码实现 <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8" /><meta http-equiv"X-…

重庆交通大学24计算机考研数据速览,专硕第二年招生,复试线321分!

重庆交通大学&#xff08;Chongqing Jiaotong University&#xff0c;CQJTU&#xff09;&#xff0c;是由重庆市人民政府和中华人民共和国交通运输部共建的一所交通特色、以工为主的多科性大学&#xff0c;入选“中西部高校基础能力建设工程”、“卓越工程师教育培养计划”、国…

用jsp实现删除数据库表中的一行

话不多说&#xff0c;直接上图 一.思路 运用<a>标签将数据送到目标页面&#xff0c;实现对一行的删除。 语法&#xff1a; 1:form表单提交到的目标页面 2&#xff1a;输入主码的input的id 3:获取主码的值 <a href"1&#xff1f;2<%3%>">删除</a…

七天速通javaSE:第四天 递归算法

文章目录 前言一、递归的介绍二、递归模型&#xff08;n!&#xff09;1 阶乘的定义&#xff1a;2. 阶乘的递归代码实现3. 递推与回归的内部逻辑三、练习 前言 本文将学习递归算法。在计算机科学中&#xff0c;递归算法是一种将问题不断分解 为同一类子问题来解决问题的方法。递…

网站推广如何做?这七个方法要知道

在出海独立站商家中&#xff0c;推广是必不可少的环节。在你完成网站的搭建&#xff0c;产品的上架&#xff0c;以及网站的运营和优化后&#xff0c;你就可以开始着手推广你的网站了。你的网站是承载你的品牌和产品的主要平台&#xff0c;因此&#xff0c;你需要根据你的品牌和…

Windows Nginx更新版本

一、准备新版安装包 nginx: downloadhttps://nginx.org/en/download.html 二、升级Nginx 1、备份原Nginx 2、上传新版Nginx 上传并解压 3、更新版本 1&#xff09;原文件夹更新 1.关闭nginx 查看原版本号 nginx -v 命今关闭 nginx -s stop 确认进程里没有nginx&#…

ZGC垃圾收集的主要流程

值得说明的是&#xff0c;在执行就地迁移时&#xff0c;ZGC 必须首先压缩指定为对象迁移区域内的对象&#xff0c;这可能会对性能产生负面影响。增加堆大小可以帮助 ZGC 避免使用就地迁移。 如上图&#xff0c;ZGC 的工作流程主要包括以下几个步骤&#xff1a; &#xff08;STW…

DVWA 靶场 SQL Injection 通关解析

前言 DVWA代表Damn Vulnerable Web Application&#xff0c;是一个用于学习和练习Web应用程序漏洞的开源漏洞应用程序。它被设计成一个易于安装和配置的漏洞应用程序&#xff0c;旨在帮助安全专业人员和爱好者了解和熟悉不同类型的Web应用程序漏洞。 DVWA提供了一系列的漏洞场…

Python+Vue+Springboot实现电脑端微信好友导入导出

主要实现思路是使用python的自动化库uiautomation进行客户端抓取联系人&#xff0c;vue做管理界面&#xff0c;springboot做后端服务。 截图如下 登录&#xff1a; 首页 好友导出 不足之处就是只有windows版本&#xff0c;mac上还不行 而且谷歌和edge浏览器的效果是最好的&a…

面试-Java线程池

1.利用Excutors创建不同的线程池满足不同场景的需求 分析&#xff1a; 如果并发的请求的数量非常多&#xff0c;但每个线程执行的时间非常短&#xff0c;这样就会频繁的创建和销毁线程。如此一来&#xff0c;会大大降低系统的效率。 可能出现&#xff0c;服务器在为每个线程创建…

LAMP架构的源码编译环境下部署Discuz论坛

一、LAMP架构 LAMP架构是一种常见的用于构建动态网站的技术栈 组成功能Linux&#xff08;操作系统&#xff09;LAMP 架构的基础&#xff0c;用于托管 Web 服务器和应用程序Apache&#xff08;Web服务器&#xff09;接收和处理客户端请求&#xff0c;并将静态和动态内容发送给…

20240626让飞凌的OK3588-C开发板在相机使用1080p60分辨率下预览

20240626让飞凌的OK3588-C开发板在相机使用1080p60分辨率下预览 2024/6/26 15:15 4.2.1 全编译测试 在源码路径内&#xff0c;提供了编译脚本 build.sh&#xff0c;运行该脚本对整个源码进行编译&#xff0c;需要在终端切换到解压 出来的源码路径&#xff0c;找到 build.sh 文件…

设备智能化:中国星坤线缆组件的解决方案!

在当今快速发展的电子行业中&#xff0c;产品小型化和成本效益是制造商追求的两大目标。中国星坤端子电缆组件以其灵活性和高效性&#xff0c;为电子设备制造商提供了一种理想的解决方案。本文将探讨星坤端子电缆组件的优势以及其在不同电子设备中的应用。 端子线&#xff1a;小…

Spring AI 实现调用openAi 多模态大模型

什么是多模态? 多模态(Multimodal)指的是数据或信息的多种表现形式。在人工智能领域,我们经常会听到这个词,尤其是在近期大型模型(如GPT-4)开始支持多模态之后。 模态:模态是指数据的一种形式,例如文本、图像、音频等。每一种形式都是一种模态。多模态:多模态就是将…

uniapp地图点击获取位置

主页面 <view class"right-content" click.stop"kilometer(item)"><view class"km">{{item.distance||0}}km</view><image src"../../static/map.png" mode""style"width: 32rpx; height: 32rpx…

Linux-笔记 OverlayFS文件系统入门

目录 前言 主要概念 工作原理 特点特性 1、上下合并 2、同名文件覆盖 3、同名目录合并 4、写时拷贝 实操入门 内核配置 挂载文件系统 验证 1、同名文件覆盖 2、同名目录合并 3、写时拷贝 1&#xff09;验证新增文件或目录 2&#xff09;验证修改文件 3&…

2024最新谷歌镜像网站入口分享

google谷歌搜索引擎最新可用镜像站列表&#xff1a;&#xff08;注意不要登录账号&#xff0c;镜像站并非谷歌官方网站&#xff09; 谷歌镜像网站1&#xff1a;https://google.cloudnative.love/ 谷歌镜像网站2&#xff1a;https://gsearch.g.shellten.top/ 谷歌镜像网站3&…

【论文阅读】--Popup-Plots: Warping Temporal Data Visualization

弹出图&#xff1a;扭曲时态数据可视化 摘要1 引言2 相关工作3 弹出图3.1 椭球模型3.1.1 水平轨迹3.1.2 垂直轨迹3.1.3 组合轨迹 3.2 视觉映射与交互 4 实施5 结果6 评估7 讨论8 结论和未来工作致谢参考文献 期刊: IEEE Trans. Vis. Comput. Graph.&#xff08;发表日期: 2019&…