栏目分类:
子分类:
返回
终身学习网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
终身学习网 > IT > 软件开发 > 后端开发 > Python

用不到300行代码让C++在几乎不修改原来代码的情况下实现简单的反射与序列化

Python 更新时间:发布时间: 百科书网 趣学号

C++自己并没有反射和序列化
而很多相关的实现代码非常复杂,而且含有大量模板和宏
比如这样的

(无意冒犯,只是拿来举例,代码的作者还是很棒帮的,后面写了更棒的反射代码)
反射的注册部分写得很复杂,如果我有一个已经写了很多的项目,那么涉及到的代码改动会非常大
而且很多都用了很多非常黑科技的模板技巧眼花缭乱的,满篇都是decltype,auto,constexpr,typeid
当然毕竟人家可以实现编译期反射,我这个不能。
但是从某种意义上来说,我觉得是可以的,只要想办法搞个编译期的unordered_map
所以在国庆节期间在家琢磨了一下如何实现一个使用起来简洁明了的反射
以及序列化和反序列化(反射一个很大的用途不就是干这个哈哈哈)
因为当前的主要目的其实是实现序列化和反序列化,所以暂时没有对成员函数进行反射
(即通过字符串来调用成员函数,这里会涉及到函数重载的问题,暂时还没想好解决办法)
目前还没有认真的进行debug和优化,只是暂时性的实现了简单的功能o( ̄▽ ̄)ブ
反正分享一个小demo,大家看看就好啦,代码采用了c++14的语法
首先看看我的代码实际使用上的简洁性:

#include
#include"lang/reflectable.h"  //反射的实现,实现了序列化,反序列化还待补充
struct TestA:Reflectable //步骤1:继承Reflectable
{
	TestA(int x=10,float y=12,std::string z="sjf"):x(x),y(y),z(z),t(new int(123456))
	{}
	Config get_config() //步骤2:实现方法 get_config
	{    
		Config config=Reflectable::get_config(this);//类似python中config=super(xxx,self).get_config()
		config.update({                             //添加属性,Config可以看成一个哈希表
			{"x",x},                                //此处同样模仿了python的dict格式
			{"y",y},                               
			{"z",z},
			{"t",t}
		});
		return config;
	}
	int x;
	float y;
	std::string z;
	int*t;
}; 
struct TestB:Reflectable
{
	TestB(){x=nullptr);}
	Config get_config()
	{
		Config config=Reflectable::get_config(this);
		config.update({ 
			{"x",x},
			{"y",y}
		});
		return config;
	}
	int*x;   
	TestA y; //稍微复杂一点的情况,Test*y的情况这个暂时没处理
};
using namespace std;
int main()
{
	Reflectable::Regist(); //步骤3,简单的注册一下,不注册就不会生成相关信息,直到调用一次get_config 
	try                                 //以及如果后续不调用get_config(),就不会生成反射相关的信息
	{                                   //所以并不是继承Reflectable就一定生成反射
		auto a=*(TestA*)Reflectable::get_instance("TestA"); //调用构造函数,没有参数被传递
		TestB b; 
		Reflectable::set_field(a,"x",2020);   //通过字符串给成员变量赋值
		Reflectable::set_field(a,"y",12.5f);
		Reflectable::set_field(a,"z",std::string("test"));
		Reflectable::set_field(a,"t",new int(45));
	
		cout<(a,"x")<(a,"y")<(a,"z")<(a,"t")< 

输出结果如下:

把null替换为None,放进python中能够正常运行,说明序列化后的格式没问题

不过跟python的json序列化的结果有一点出入
就是json里面只有字符串是双引号括起来的,我这里全括起来了
这是因为在当前反射的代码下暂时是不能得到更详细的(是否是字符串)的类型信息
得新写一个序列化的类别Serializable:public Reflectable 来解决(先占坑)
代码实现思路:
首先程序调用

	Reflectable::Regist(); //初始化 

这个类的实现如下

#include
#define GET_TYPE_NAME(type)
abi::__cxa_demangle(typeid(type).name(),0,0,0) //得到完整的类型名称

template
struct Reflectable::Regist
{
	Regist()
	{
		T object;
		object.get_config();//必须调用get_config,才能建立类型信息,所以这里必须先调用一次config在运行时获得类型信息 
		Reflectable::default_constructors[GET_TYPE_NAME(T)]=[](void)->void* //记录默认构造函数 
		{
			return (void*)(new T());
		};
		Regist();
	}
};

template
struct Reflectable::Regist
{
	Regist()
	{
		T object;
		object.get_config();
		Reflectable::default_constructors[GET_TYPE_NAME(T)]=[](void)->void* //默认构造函数 
		{
			return (void*)(new T());
		};
	}
};

就是循环把每个类都创建一个实例,然后调用get_config方法
第一次get_config会额外把属性的类型信息等记录下来,后续调用就只是得到一个Config字典
Reflectable::default_constructors是一个哈希表,记录构造函数,不一定是无参构造函数或者默认构造函数,反正是一个可以不传参数的构造函数

	using ClassName=std::string;
	static std::unordered_map>default_constructors;

接下来就是get_config里面的事情

	Config get_config()
	{
		Config config=Reflectable::get_config(this);
		config.update({
			{"x",x},
			{"y",y},
			{"z",z},
			{"t",t}
		});
		return config;
	}

Regist里面调用了T(Reflectable的子类)的get_config,然后子类又先调用了父类Reflectable的get_config
Reflect的声明如下

struct Reflectable
{
	template
	struct Regist;
	template
	struct Regist;
	template
	Config get_config(const T*object)const;//得到T的类型信息和名称,判断T类型的属性键值对是否建立,没有建立就建立(只建立一次) 
	template
	static void set_field(ClassType&object,std::string field_name,FieldType&&data);//设置属性值,因为已经有类型信息,所以不需要调用constructor[type]里面的函数来构造 
	template
	static void set_field(ClassType&object,std::string field_name,FieldType*data);//注意:并不会处理原来的指针指向的地址空间 
	template
	static FieldType&get_field(ClassType&object,std::string field_name); 
	static void*get_instance(std::string class_name);
private:	
	using ClassName=std::string;
	using FieldName=std::unordered_map>;
	static std::unordered_mapfield;//(类名称,方法名称)->(转化为字符串的属性值,地址偏移量)
	static std::unordered_map>assigns;//反序列化用,暂不管
	static std::unordered_map>default_constructors;//构造函数
};
//三个静态属性记录了所有class和他的所有属性的值(字符串形式)和地址偏移量
//相比于记录每个类的每个属性地址,其实只需要记录每一个属性的地址偏移量
//然后所有对象都可以用对象+地址偏移量来访问属性

get_config方法实现如下:

template
Config Reflectable::get_config(const T*object)const
{
	std::string class_name=GET_TYPE_NAME(T);//获得类名称
	Config config(
		field.find(class_name)==field.end()?&field[class_name]:nullptr,object);//如果不存在就创建,否则不做操作,这样保证field[class_name]只会被初始化一次
	config.update({{"class_name",class_name}});//把类型自己的名称加入到config中
	return config;
}

这个函数里面做的事情就是把自己的类型信息记录,然后将field[class_name]地址传入进行初始化
如果已经被初始化了,就跳过
接下来看config_update,Reflectable对象只记录字符串和函数指针,但不参与这些信息的获取
Config声明如下:

class Config//本身是一个键值对,但在每一个类型第一次调用的时候生成运行时的一些反射相关的信息 
{            /
private:
	std::unordered_mapconfig;
	std::unordered_map>*field_info;//field_info只提供基本类型的还原函数,其他的直接调用from_config递归还原(反序列化阶段) 
	std::size_t class_header_address;
	std::size_t class_size;
};

Config::Config实现:

template
Config::Config(std::unordered_map>*field_info,T*object): //在第一次被调用的时候,需要去创建类型信息 
	field_info(field_info),                                                                             //为后面的反序列化做准备 
	class_header_address((std::size_t)(object)),                                                        //field_info只提供基本类型的还原函数,其他的直接调用from_config递归还原
	class_size(sizeof(T))
	{}

在这里Config对象获得了对象地址和对象指针大小
Config::update实现:

void Config::update(const std::initializer_list&pairs)//添加变量对,config.update({{"namea",this->name1},{"name2",this->name2}});
{
	for(auto&it:pairs)
	{
		config[it.key]=it.value;
		if(field_info!=nullptr)                                //只被创建一次 c
		{
			(*field_info)[it.key].first=it.type;               //用于from_config中间接通过字符串得到类型信息 
			(*field_info)[it.key].second=it.address-this->class_header_address; //地址偏移量,用来访问成员变量 
		}
	}
}

这里就成功拿到了type和offset信息
记录类型信息是为了反序列化,把Json字符串还原为对象,Json字符串中并没有类型信息,况且也是通过字符串来遍历属性并还原,所以需要把类型信息记录下来,具体而言是在上面的assign中完成赋值
以及还未实现的字符串反序列化为对应类型的变量

啊说了这么多,其实这一堆几乎都是为了让代码使用起来更友好
具体的功能实现都在ConfigPair里面了o( ̄▽ ̄)ブ

struct ConfigPair
{
public:
	template
	ConfigPair(std::string name,const T&object);
	std::string key;    //成员变量名称,与声明的名称对应 
	std::string value;  //成员函数的值转化为字符串的结果(如果是基本类型,则直接转化为字符串,否则为嵌套字典结构) 
	std::string type;   //类型,typeid(type).name(),相当于记录下类型了,runtime时反序列化会用到 
	std::size_t address;//地址,后面将用来计算成员变量地址偏移量,反序列化的时候通过(void*)(对象地址+偏移量)来访问成员变量 
private:
	template
	struct has_member_get_config //编译期检测是否有成员函数get_config,如果value=true,说明该类型是支持序列化的非基本类型(实现了get_config方法) 
	{
	    template
	        static auto Check(int)->decltype(std::declval().get_config(),std::true_type());
	    template
	        static std::false_type Check(...);
	    static constexpr int value = std::is_same(0)),std::true_type>::value;
	};
	template
	using serializable_type=typename std::enable_if::value,T>::type;  //可序列化非基本类型 
	template
	using fundamental_type=typename std::enable_if::value,T>::type;     //基本类型 
	template
	using std_string_type=typename std::enable_if::value,T>::type; //字符串 
	template 
	using remove_pointer=typename std::remove_pointer::type;                                //移除指针 
	template
	using ptr_fundamental_type=typename std::enable_if::value&std::is_fundamental>::value,T>::type;//基本类型的指针
	template
	using unsupported_type=typename std::enable_if,T>::value|
		std::is_same,T>::value|
		std::is_same,T>::value|
		std::is_same,T>::value
	),T>::type;
	template
	static auto get_config_string(const fundamental_type&field);//基本类型 
	template
	static auto get_config_string(const std_string_type&field);//std::string 
	template
	static auto get_config_string(const ptr_fundamental_type&field);//基本类型的指针 
	template
	static auto get_config_string(const serializable_type&field);//基本类型的指针 
	template
	static auto get_config_string(const unsupported_type&field);//其他 
};

这里面涉及到很多跟类型相关的模板,分为基本类型,基本类型的指针,Reflectable子类(实现了get_config方法),Reflectable子类指针,std::string,其他不支持的类型,然后处理方式上有所差异
类似int float之类的基本类型,变为字符串是很容易的

template
auto ConfigPair::get_config_string(const fundamental_type&field)//基本类型 
{
	std::ostringstream oss;
	oss< 
template
auto ConfigPair::get_config_string(const ptr_fundamental_type&field)//基本类型的指针 
{
	if(field==nullptr) //空指针 
		return std::string("null"); 
	std::ostringstream oss;
	oss<<*field;
	return oss.str();
}

对于Reflectable子类,递归之

template
auto ConfigPair::get_config_string(const serializable_type&field)//可序列化的非基本类型 
{
	std::ostringstream oss;
	return ((T)field).get_config().serialized_to_string(false);
}

ConfigPair::ConfifPair

template
ConfigPair::ConfigPair(std::string name,const T&object):
	key(name), //名称
	value(ConfigPair::get_config_string(object)), //转化为字符串的值
	type(GET_TYPE_NAME(T)), //类型对应的字符串
	address((std::size_t)&object) //地址,用来计算地址偏移量
	{}

这里的构造函数是模板函数,所以可以在编译器捕获到成员变量的类型信息
也就是

		config.update({
			{"x",x},
			{"y",y},
			{"z",z},
			{"t",t}
		});

实际上应该是

		config.update(std::initializer_list({
			ConfigPair({"x",x}),
			ConfigPair({"y",y}),
			ConfigPair({"z",z}),
			ConfigPair({"t",t})
		)});

每一行其实调用了不同的构造函数,因此类型信息得以被记录下来
同样不同的类型采用不同的序列化和反序列化
整个问题就解决了,可以在原来的代码的基础上添加少量修改,就很方便的实现了序列化
至于Config这个"字典"转化为字符串,这就简单很多了,直接遍历输出就行

std::string Config::serialized_to_string(bool first_nested_layer)const //序列化的内容,后面会移动到Serializable::xxx() 
{
	std::ostringstream oss;
	if(first_nested_layer)
	{
		oss<<"{n";
		for(auto&it:config)
			oss<<"""< 

完结撒花!

以下是完整的源码
main.cpp

//#define DEBUG_TENSOR
//#define DEBUG_SHARED_DATA
#include
#include"lang/reflectable.h"
struct TestA:Reflectable
{
	TestA(int x=10,float y=12,std::string z="sjf"):x(x),y(y),z(z),t(new int(123456))
	{}
	Config get_config()
	{
		Config config=Reflectable::get_config(this);
		config.update({
			{"x",x},
			{"y",y},
			{"z",z},
			{"t",t}
		});
		return config;
	}
	int x;
	float y;
	std::string z;
	int*t;
}; 
struct TestB:Reflectable
{
	TestB(){x=nullptr;}
	Config get_config()
	{
		Config config=Reflectable::get_config(this);
		config.update({
			{"x",x},
			{"y",y}
		});
		return config;
	}
	int*x;
	TestA y;
};
using namespace std;  
int main() 
{
	Reflectable::Regist(); //初始化   
	try
	{
		auto a=*(TestA*)Reflectable::get_instance("TestA"); //构造函数
		TestB b; 
		Reflectable::set_field(a,"x",2020);
		Reflectable::set_field(a,"y",12.5f);
		Reflectable::set_field(a,"z",std::string("test"));
		Reflectable::set_field(a,"t",new int(45));
	
		cout<(a,"x")<(a,"y")<(a,"z")<(a,"t")< 

configpair.h

#ifndef __CONFIGPAIR_H__
#define __CONFIGPAIR_H__
#include
#include
#include
#include 
#include
#define GET_TYPE_NAME(type)
abi::__cxa_demangle(typeid(type).name(),0,0,0)
struct ConfigPair
{
public:
	template
	ConfigPair(std::string name,const T&object);
	std::string key;    //成员变量名称,与声明的名称对应 
	std::string value;  //成员函数的值转化为字符串的结果(如果是基本类型,则直接转化为字符串,否则为嵌套字典结构) 
	std::string type;   //类型,typeid(type).name(),相当于记录下类型了,runtime时反序列化会用到 
	std::size_t address;//地址,后面将用来计算成员变量地址偏移量,反序列化的时候通过(void*)(对象地址+偏移量)来访问成员变量 
private:
	template
	struct has_member_get_config //编译期检测是否有成员函数get_config,如果value=true,说明该类型是支持序列化的非基本类型(实现了get_config方法) 
	{
	    template
	        static auto Check(int)->decltype(std::declval().get_config(),std::true_type());
	    template
	        static std::false_type Check(...);
	    static constexpr int value = std::is_same(0)),std::true_type>::value;
	};
	template
	using serializable_type=typename std::enable_if::value,T>::type;  //可序列化非基本类型 
	template
	using fundamental_type=typename std::enable_if::value,T>::type;     //基本类型 
	template
	using std_string_type=typename std::enable_if::value,T>::type; //字符串 
	template 
	using remove_pointer=typename std::remove_pointer::type;                                //移除指针 
	template
	using ptr_fundamental_type=typename std::enable_if::value&std::is_fundamental>::value,T>::type;//基本类型的指针
	template
	using unsupported_type=typename std::enable_if,T>::value|
		std::is_same,T>::value|
		std::is_same,T>::value|
		std::is_same,T>::value
	),T>::type;
	template
	static auto get_config_string(const fundamental_type&field);//基本类型 
	template
	static auto get_config_string(const std_string_type&field);//std::string 
	template
	static auto get_config_string(const ptr_fundamental_type&field);//基本类型的指针 
	template
	static auto get_config_string(const serializable_type&field);//基本类型的指针 
	template
	static auto get_config_string(const unsupported_type&field);//其他 
};
//只支持基本类型(int,double,...)及其指针形式,以及std::string 
//其他类型均认为是复合类型,从from_config中递归恢复,否则从constructors中找到合适的... 
template
ConfigPair::ConfigPair(std::string name,const T&object):
	key(name),
	value(ConfigPair::get_config_string(object)),
	type(GET_TYPE_NAME(T)),
	address((std::size_t)&object)
	{
	//	std::cout<<"name:"<
auto ConfigPair::get_config_string(const fundamental_type&field)//基本类型 
{
	std::ostringstream oss;
	oss<
auto ConfigPair::get_config_string(const std_string_type&field)//std::string 
{
	return field;
}
template
auto ConfigPair::get_config_string(const ptr_fundamental_type&field)//基本类型的指针 
{
	std::ostringstream oss;
	oss<<*field;
	return oss.str();
}
template
auto ConfigPair::get_config_string(const serializable_type&field)//可序列化的非基本类型 
{
	std::ostringstream oss;
	return ((T)field).get_config().serialized_to_string(false);
}
template
auto ConfigPair::get_config_string(const unsupported_type&field)//其他 
{
	return "";
}		
#endif

config.h

#ifndef __CONFIG_H__
#define __CONFIG_H__
#include"configpair.h"
#include
class Config//本身是一个键值对,但在每一个类型第一次调用的时候生成运行时的一些反射相关的信息 
{            /
private:
	std::unordered_mapconfig;
	std::unordered_map>*field_info;//field_info只提供基本类型的还原函数,其他的直接调用from_config递归还原(反序列化阶段) 
	std::size_t class_header_address;
	std::size_t class_size;
};
template
Config::Config(std::unordered_map>*field_info,T*object): //在第一次被调用的时候,需要去创建类型信息 
	field_info(field_info),                                                                             //为后面的反序列化做准备 
	class_header_address((std::size_t)(object)),                                                        //field_info只提供基本类型的还原函数,其他的直接调用from_config递归还原
	class_size(sizeof(T)){}
void Config::update(const std::initializer_list&pairs)//添加变量对,config.update({{"namea",this->name1},{"name2",this->name2}});
{
	for(auto&it:pairs)
	{
		config[it.key]=it.value;
		if(field_info!=nullptr)                                //只被创建一次 c
		{
			(*field_info)[it.key].first=it.type;               //用于from_config中间接通过字符串得到类型信息 
			(*field_info)[it.key].second=it.address-this->class_header_address; //地址偏移量,用来访问成员变量 
		}
	}
}
std::string&Config::operator[](const std::string&key) //键值对 
{
	return config[key];
}
std::string Config::serialized_to_string(bool first_nested_layer)const //序列化的内容,后面会移动到Serializable::xxx() 
{
	std::ostringstream oss;
	if(first_nested_layer)
	{
		oss<<"{n";
		for(auto&it:config)
			oss<<"""< 

reflectable.h

#ifndef __REFLECT_H__
#define __REFLECT_H__
#include"config.h"
#include
struct Reflectable
{
	template
	struct Regist;
	template
	struct Regist;
	template
	Config get_config(const T*object)const;//得到T的类型信息和名称,判断T类型的属性键值对是否建立,没有建立就建立(只建立一次) 
	template
	static void set_field(ClassType&object,std::string field_name,FieldType&&data);//设置属性值,因为已经有类型信息,所以不需要调用constructor[type]里面的函数来构造 
	template
	static void set_field(ClassType&object,std::string field_name,FieldType*data);//注意:并不会处理原来的指针指向的地址空间 
	template
	static FieldType&get_field(ClassType&object,std::string field_name); 
	static void*get_instance(std::string class_name);
private:	
	using ClassName=std::string;
	using FieldName=std::unordered_map>;
	static std::unordered_mapfield;
	static std::unordered_map>assigns;
	static std::unordered_map>default_constructors;
};
std::unordered_map>>Reflectable::field;
std::unordered_map>Reflectable::assigns={//for fundamental
	{GET_TYPE_NAME(int),[](void*p,void*q){*(int*)p=*(int*)q;}},
	{GET_TYPE_NAME(float),[](void*p,void*q){*(float*)p=*(float*)q;}},
	{GET_TYPE_NAME(double),[](void*p,void*q){*(double*)p=*(double*)q;}},
	{GET_TYPE_NAME(char),[](void*p,void*q){*(char*)p=*(char*)q;}},
	{GET_TYPE_NAME(std::string),[](void*p,void*q){*(std::string*)p=*(std::string*)q;}},
};
std::unordered_map>Reflectable::default_constructors;
template
Config Reflectable::get_config(const T*object)const
{
	std::string class_name=GET_TYPE_NAME(T);
	Config config(
		field.find(class_name)==field.end()?&field[class_name]:nullptr,object);//如果不存在就创建,否则不做操作 
	config.update({{"class_name",class_name}});
	return config;
}
template
void Reflectable::set_field(ClassType&object,std::string field_name,FieldType&&data)
{
	std::string class_name=GET_TYPE_NAME(ClassType);
	std::size_t offset=Reflectable::field[class_name][field_name].second;
	std::string type=Reflectable::field[class_name][field_name].first;
	*(FieldType*)((std::size_t)(&object)+offset)=data;
}
template
void Reflectable::set_field(ClassType&object,std::string field_name,FieldType*data)//注意:并不会处理原来的指针指向的地址空间 
{
	std::string class_name=GET_TYPE_NAME(ClassType);
	std::size_t offset=Reflectable::field[class_name][field_name].second;
	std::string type=Reflectable::field[class_name][field_name].first;
	*(FieldType**)((std::size_t)(&object)+offset)=data;
}

template
FieldType&Reflectable::get_field(ClassType&object,std::string field_name)
{
	std::string class_name=GET_TYPE_NAME(ClassType);
	std::size_t offset=Reflectable::field[class_name][field_name].second;
	std::string type=Reflectable::field[class_name][field_name].first;
	return *(FieldType*)((std::size_t)(&object)+offset);
}
void*Reflectable::get_instance(std::string class_name)
{
	return Reflectable::default_constructors[class_name]();
}
template
struct Reflectable::Regist
{
	Regist()
	{
		T object;
		object.get_config();//必须调用get_config,才能建立类型信息,所以这里必须先调用一次Reflectable的get_config 
		Reflectable::default_constructors[GET_TYPE_NAME(T)]=[](void)->void*{return (void*)(new T());}; //默认构造函数 
		Regist();
	}
};
template
struct Reflectable::Regist
{
	Regist()
	{
		T object;
		object.get_config();
		Reflectable::default_constructors[GET_TYPE_NAME(T)]=[](void)->void* //默认构造函数 
		{
			return (void*)(new T());
		};
	}
};
#endif
转载请注明:文章转载自 www.051e.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 ©2023-2025 051e.com

ICP备案号:京ICP备12030808号