C++自己并没有反射和序列化 而很多相关的实现代码非常复杂,而且含有大量模板和宏 比如这样的 (无意冒犯,只是拿来举例,代码的作者还是很棒帮的,后面写了更棒的反射代码) 反射的注册部分写得很复杂,如果我有一个已经写了很多的项目,那么涉及到的代码改动会非常大 而且很多都用了很多非常黑科技的模板技巧眼花缭乱的,满篇都是decltype,auto,constexpr,typeid 当然毕竟人家可以实现编译期反射,我这个不能。 但是从某种意义上来说,我觉得是可以的,只要想办法搞个编译期的unordered_map 所以在国庆节期间在家琢磨了一下如何实现一个使用起来简洁明了的反射 以及序列化和反序列化(反射一个很大的用途不就是干这个哈哈哈) 因为当前的主要目的其实是实现序列化和反序列化,所以暂时没有对成员函数进行反射 (即通过字符串来调用成员函数,这里会涉及到函数重载的问题,暂时还没想好解决办法) 目前还没有认真的进行debug和优化,只是暂时性的实现了简单的功能o( ̄▽ ̄)ブ 反正分享一个小demo,大家看看就好啦,代码采用了c++14的语法 首先看看我的代码实际使用上的简洁性:
#include<iostream>
#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<TestA,TestB>();
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<<" "<<Reflectable::get_field<int>(a,"x")<<endl;
cout<<a.y<<" "<<Reflectable::get_field<float>(a,"y")<<endl;
cout<<a.z<<" "<<Reflectable::get_field<std::string>(a,"z")<<endl;
cout<<*a.t<<" "<<*Reflectable::get_field<int*>(a,"t")<<endl;
cout<<a.get_config().serialized_to_string();
cout<<"---------------------------------------\n";
cout<<b.get_config().serialized_to_string();
}
catch(exception&e)
{
cout<<e.what();
}
}
输出结果如下:
把null替换为None,放进python中能够正常运行,说明序列化后的格式没问题
不过跟python的json序列化的结果有一点出入 就是json里面只有字符串是双引号括起来的,我这里全括起来了 这是因为在当前反射的代码下暂时是不能得到更详细的(是否是字符串)的类型信息 得新写一个序列化的类别Serializable:public Reflectable 来解决(先占坑) 代码实现思路: 首先程序调用
Reflectable::Regist<TestA,TestB>();
这个类的实现如下
#include<cxxabi.h>
#define GET_TYPE_NAME(type)\
abi::__cxa_demangle(typeid(type).name(),0,0,0)
template<typename T,typename ...Args>
struct Reflectable::Regist
{
Regist()
{
T object;
object.get_config();
Reflectable::default_constructors[GET_TYPE_NAME(T)]=[](void)->void*
{
return (void*)(new T());
};
Regist<Args...>();
}
};
template<typename T>
struct Reflectable::Regist<T>
{
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<ClassName,std::function<void*(void)>>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<typename T,typename ...Args>
struct Regist;
template<typename T>
struct Regist<T>;
template<typename T>
Config get_config(const T*object)const;
template<typename ClassType,typename FieldType>
static void set_field(ClassType&object,std::string field_name,FieldType&&data);
template<typename ClassType,typename FieldType>
static void set_field(ClassType&object,std::string field_name,FieldType*data);
template<typename FieldType,typename ClassType>
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<std::string,std::pair<std::string,std::size_t>>;
static std::unordered_map<ClassName,FieldName>field;
static std::unordered_map<ClassName,std::function<void(void*,void*)>>assigns;
static std::unordered_map<ClassName,std::function<void*(void)>>default_constructors;
};
get_config 方法实现如下:
template<typename T>
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;
}
这个函数里面做的事情就是把自己的类型信息记录,然后将field[class_name] 地址传入进行初始化 如果已经被初始化了,就跳过 接下来看config_update ,Reflectable对象只记录字符串和函数指针,但不参与这些信息的获取 Config 声明如下:
class Config
{
public:
Config(){}
template<typename T>
Config(std::unordered_map<std::string,std::pair<std::string,std::size_t>>*field_info,T*object);
std::string serialized_to_string(bool first_nested_layer=true)const;
std::string&operator[](const std::string&key);
void update(const std::initializer_list<ConfigPair>&pairs);
private:
std::unordered_map<std::string,std::string>config;
std::unordered_map<std::string,std::pair<std::string,std::size_t>>*field_info;
std::size_t class_header_address;
std::size_t class_size;
};
Config::Config 实现:
template<typename T>
Config::Config(std::unordered_map<std::string,std::pair<std::string,std::size_t>>*field_info,T*object):
field_info(field_info),
class_header_address((std::size_t)(object)),
class_size(sizeof(T))
{}
在这里Config 对象获得了对象地址和对象指针大小 Config::update 实现:
void Config::update(const std::initializer_list<ConfigPair>&pairs)
{
for(auto&it:pairs)
{
config[it.key]=it.value;
if(field_info!=nullptr)
{
(*field_info)[it.key].first=it.type;
(*field_info)[it.key].second=it.address-this->class_header_address;
}
}
}
这里就成功拿到了type和offset信息 记录类型信息是为了反序列化,把Json字符串还原为对象,Json字符串中并没有类型信息,况且也是通过字符串来遍历属性并还原,所以需要把类型信息记录下来,具体而言是在上面的assign中完成赋值 以及还未实现的字符串反序列化为对应类型的变量
啊说了这么多,其实这一堆几乎都是为了让代码使用起来更友好 具体的功能实现都在ConfigPair 里面了o( ̄▽ ̄)ブ
struct ConfigPair
{
public:
template<typename T>
ConfigPair(std::string name,const T&object);
std::string key;
std::string value;
std::string type;
std::size_t address;
private:
template<typename T>
struct has_member_get_config
{
template<typename U>
static auto Check(int)->decltype(std::declval<U>().get_config(),std::true_type());
template<typename U>
static std::false_type Check(...);
static constexpr int value = std::is_same<decltype(Check<T>(0)),std::true_type>::value;
};
template<typename T>
using serializable_type=typename std::enable_if<has_member_get_config<T>::value,T>::type;
template<typename T>
using fundamental_type=typename std::enable_if<std::is_fundamental<T>::value,T>::type;
template<typename T>
using std_string_type=typename std::enable_if<std::is_same<T,std::string>::value,T>::type;
template<typename T>
using remove_pointer=typename std::remove_pointer<T>::type;
template<typename T>
using ptr_fundamental_type=typename std::enable_if<std::is_pointer<T>::value&std::is_fundamental<remove_pointer<T>>::value,T>::type;
template<typename T>
using unsupported_type=typename std::enable_if<not(
std::is_same<fundamental_type<T>,T>::value|
std::is_same<std_string_type<T>,T>::value|
std::is_same<ptr_fundamental_type<T>,T>::value|
std::is_same<serializable_type<T>,T>::value
),T>::type;
template<typename T>
static auto get_config_string(const fundamental_type<T>&field);
template<typename T>
static auto get_config_string(const std_string_type<T>&field);
template<typename T>
static auto get_config_string(const ptr_fundamental_type<T>&field);
template<typename T>
static auto get_config_string(const serializable_type<T>&field);
template<typename T>
static auto get_config_string(const unsupported_type<T>&field);
};
这里面涉及到很多跟类型相关的模板,分为基本类型,基本类型的指针,Reflectable 子类(实现了get_config 方法),Reflectable 子类指针,std::string, 其他不支持的类型,然后处理方式上有所差异 类似int float之类的基本类型,变为字符串是很容易的
template<typename T>
auto ConfigPair::get_config_string(const fundamental_type<T>&field)
{
std::ostringstream oss;
oss<<field;
return oss.str();
}
template<typename T>
auto ConfigPair::get_config_string(const ptr_fundamental_type<T>&field)
{
if(field==nullptr)
return std::string("null");
std::ostringstream oss;
oss<<*field;
return oss.str();
}
对于Reflectable子类,递归之
template<typename T>
auto ConfigPair::get_config_string(const serializable_type<T>&field)
{
std::ostringstream oss;
return ((T)field).get_config().serialized_to_string(false);
}
ConfigPair::ConfifPair
template<typename T>
ConfigPair::ConfigPair(std::string name,const T&object):
key(name),
value(ConfigPair::get_config_string<T>(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>({
ConfigPair({"x",x}),
ConfigPair({"y",y}),
ConfigPair({"z",z}),
ConfigPair({"t",t})
)});
每一行其实调用了不同的构造函数,因此类型信息得以被记录下来 同样不同的类型采用不同的序列化和反序列化 整个问题就解决了,可以在原来的代码的基础上添加少量修改,就很方便的实现了序列化 至于Config 这个"字典"转化为字符串,这就简单很多了,直接遍历输出就行
std::string Config::serialized_to_string(bool first_nested_layer)const
{
std::ostringstream oss;
if(first_nested_layer)
{
oss<<"{\n";
for(auto&it:config)
oss<<"\""<<it.first<<"\":"<<it.second<<",\n";
oss<<"}\n";
}
else
{
oss<<"{";
for(auto&it:config)
oss<<"\""<<it.first<<"\":"<<it.second<<",";
oss<<"}";
}
return oss.str();
}
完结撒花!
以下是完整的源码 main.cpp
#include<iostream>
#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<TestA,TestB>();
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<<" "<<Reflectable::get_field<int>(a,"x")<<endl;
cout<<a.y<<" "<<Reflectable::get_field<float>(a,"y")<<endl;
cout<<a.z<<" "<<Reflectable::get_field<std::string>(a,"z")<<endl;
cout<<*a.t<<" "<<*Reflectable::get_field<int*>(a,"t")<<endl;
cout<<a.get_config().serialized_to_string();
cout<<"---------------------------------------\n";
cout<<b.get_config().serialized_to_string();
}
catch(exception&e)
{
cout<<e.what();
}
}
configpair.h
#ifndef __CONFIGPAIR_H__
#define __CONFIGPAIR_H__
#include<string>
#include<typeinfo>
#include<sstream>
#include<string>
#include<cxxabi.h>
#define GET_TYPE_NAME(type)\
abi::__cxa_demangle(typeid(type).name(),0,0,0)
struct ConfigPair
{
public:
template<typename T>
ConfigPair(std::string name,const T&object);
std::string key;
std::string value;
std::string type;
std::size_t address;
private:
template<typename T>
struct has_member_get_config
{
template<typename U>
static auto Check(int)->decltype(std::declval<U>().get_config(),std::true_type());
template<typename U>
static std::false_type Check(...);
static constexpr int value = std::is_same<decltype(Check<T>(0)),std::true_type>::value;
};
template<typename T>
using serializable_type=typename std::enable_if<has_member_get_config<T>::value,T>::type;
template<typename T>
using fundamental_type=typename std::enable_if<std::is_fundamental<T>::value,T>::type;
template<typename T>
using std_string_type=typename std::enable_if<std::is_same<T,std::string>::value,T>::type;
template<typename T>
using remove_pointer=typename std::remove_pointer<T>::type;
template<typename T>
using ptr_fundamental_type=typename std::enable_if<std::is_pointer<T>::value&std::is_fundamental<remove_pointer<T>>::value,T>::type;
template<typename T>
using unsupported_type=typename std::enable_if<not(
std::is_same<fundamental_type<T>,T>::value|
std::is_same<std_string_type<T>,T>::value|
std::is_same<ptr_fundamental_type<T>,T>::value|
std::is_same<serializable_type<T>,T>::value
),T>::type;
template<typename T>
static auto get_config_string(const fundamental_type<T>&field);
template<typename T>
static auto get_config_string(const std_string_type<T>&field);
template<typename T>
static auto get_config_string(const ptr_fundamental_type<T>&field);
template<typename T>
static auto get_config_string(const serializable_type<T>&field);
template<typename T>
static auto get_config_string(const unsupported_type<T>&field);
};
template<typename T>
ConfigPair::ConfigPair(std::string name,const T&object):
key(name),
value(ConfigPair::get_config_string<T>(object)),
type(GET_TYPE_NAME(T)),
address((std::size_t)&object)
{
}
template<typename T>
auto ConfigPair::get_config_string(const fundamental_type<T>&field)
{
std::ostringstream oss;
oss<<field;
return oss.str();
}
template<typename T>
auto ConfigPair::get_config_string(const std_string_type<T>&field)
{
return field;
}
template<typename T>
auto ConfigPair::get_config_string(const ptr_fundamental_type<T>&field)
{
std::ostringstream oss;
oss<<*field;
return oss.str();
}
template<typename T>
auto ConfigPair::get_config_string(const serializable_type<T>&field)
{
std::ostringstream oss;
return ((T)field).get_config().serialized_to_string(false);
}
template<typename T>
auto ConfigPair::get_config_string(const unsupported_type<T>&field)
{
return "<unsupported type>";
}
#endif
config.h
#ifndef __CONFIG_H__
#define __CONFIG_H__
#include"configpair.h"
#include<unordered_map>
class Config
{
public:
Config(){}
template<typename T>
Config(std::unordered_map<std::string,std::pair<std::string,std::size_t>>*field_info,T*object);
std::string serialized_to_string(bool first_nested_layer=true)const;
std::string&operator[](const std::string&key);
void update(const std::initializer_list<ConfigPair>&pairs);
private:
std::unordered_map<std::string,std::string>config;
std::unordered_map<std::string,std::pair<std::string,std::size_t>>*field_info;
std::size_t class_header_address;
std::size_t class_size;
};
template<typename T>
Config::Config(std::unordered_map<std::string,std::pair<std::string,std::size_t>>*field_info,T*object):
field_info(field_info),
class_header_address((std::size_t)(object)),
class_size(sizeof(T)){}
void Config::update(const std::initializer_list<ConfigPair>&pairs)
{
for(auto&it:pairs)
{
config[it.key]=it.value;
if(field_info!=nullptr)
{
(*field_info)[it.key].first=it.type;
(*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
{
std::ostringstream oss;
if(first_nested_layer)
{
oss<<"{\n";
for(auto&it:config)
oss<<"\""<<it.first<<"\":"<<it.second<<",\n";
oss<<"}\n";
}
else
{
oss<<"{";
for(auto&it:config)
oss<<"\""<<it.first<<"\":"<<it.second<<",";
oss<<"}";
}
return oss.str();
}
#endif
reflectable.h
#ifndef __REFLECT_H__
#define __REFLECT_H__
#include"config.h"
#include<functional>
struct Reflectable
{
template<typename T,typename ...Args>
struct Regist;
template<typename T>
struct Regist<T>;
template<typename T>
Config get_config(const T*object)const;
template<typename ClassType,typename FieldType>
static void set_field(ClassType&object,std::string field_name,FieldType&&data);
template<typename ClassType,typename FieldType>
static void set_field(ClassType&object,std::string field_name,FieldType*data);
template<typename FieldType,typename ClassType>
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<std::string,std::pair<std::string,std::size_t>>;
static std::unordered_map<ClassName,FieldName>field;
static std::unordered_map<ClassName,std::function<void(void*,void*)>>assigns;
static std::unordered_map<ClassName,std::function<void*(void)>>default_constructors;
};
std::unordered_map<std::string,std::unordered_map<std::string,std::pair<std::string,std::size_t>>>Reflectable::field;
std::unordered_map<std::string,std::function<void(void*,void*)>>Reflectable::assigns={
{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<std::string,std::function<void*(void)>>Reflectable::default_constructors;
template<typename T>
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<typename ClassType,typename FieldType>
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<typename ClassType,typename FieldType>
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<typename FieldType,typename ClassType>
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<typename T,typename ...Args>
struct Reflectable::Regist
{
Regist()
{
T object;
object.get_config();
Reflectable::default_constructors[GET_TYPE_NAME(T)]=[](void)->void*{return (void*)(new T());};
Regist<Args...>();
}
};
template<typename T>
struct Reflectable::Regist<T>
{
Regist()
{
T object;
object.get_config();
Reflectable::default_constructors[GET_TYPE_NAME(T)]=[](void)->void*
{
return (void*)(new T());
};
}
};
#endif
|