Static Reflection and Serialization in C++
SetUp
在我们可以制造一个序列化函数之前,我们第一步需要去制作一些需要的可用的数据对我们,在我们的程序执行期间。
我们可以定义一个基本数据结构,它将保存这个信息。
struct Type{
std::string stringName;
TypeName enumName;
size_t size;
};
在我们的结构体中,除了保存类型的大小之外,我们还保存了类型的可读字符串名称。
第一个是类型名,第二个枚举允许我们去更加轻松地决定我们查看的类型是什么。
一旦完成,我们可以制作一个宏,允许我们快速地去实例化新的类型。
#define DEFINE_TYPE(TYPE)\
template<>\
Type* GetType<TYPE>(){\
static Type type;\
type.stringName = #TYPE;\
type.size = sizeof(TYPE);\
type.enumName = TypeName::TYPE;\
return &type;\
}\
比如,展开int8_t。
template<>
Type* GetType<int8_t>(){
static Type type;
type.stringNmae = "int8_t";
type.size = sizeof(int8_t);
type.enumName = TypeName::int8_t;;
return &type;
}
用来一些图元类型:
DEFINE_TYPE(int8_t)
DEFINE_TYPE(int16_t)
DEFINE_TYPE(int32_t)
DEFINE_TYPE(uint8_t)
DEFINE_TYPE(uint16_t)
DEFINE_TYPE(uint32_t)
目前我们有一些类型被定义,我们可以创建另一个结构体对于成员变量,对于那些我们需要在运行时访问的metadata。
struct Field
{
Type* type;
std::string name;
size_t offset;
};
//MAX_NUMBER_OF_FILEDS is arbitrarily large
struct Class {
std::array<Field, MAX_NUMBER_OF_FIELDS> fields;
};
所以,Class类,是Field的数组。
并且一些宏去快速地实例化被要求的数据:
#define BEGIN_ATTRIBUTES_FOR(CLASS) \
template<> \
Class* GetClass<CLASS>() { \
using ClassType = CLASS; \
static Class localClass; \
enum { BASE = __COUNTER__ }; \
#define DEFINE_MEMBER(NAME) \
enum { NAME##Index = __COUNTER__ - BASE - 1}; \
localClass.fields[NAME##Index].type = GetType<decltype(ClassType::NAME)>();\
localClass.fields[NAME##Index].name = { #NAME }; \
localClass.fields[NAME##Index].offset = offsetof(ClassType, NAME);\
#define END_ATTRIBUTES \
return &localClass; \
}\
__COUNTER__宏创建了字面值从零开始,在编译期间,并且每次它出现在一个文件中,它会被替换为它之前的值加+1。会被重置为0,对于每个新的类型被定义在一个文件中。
// Struct to reflect
struct TestStruct {
int32_t field1;
int16_t field2;
int8_t field3;
uint32_t field4;
uint16_t field5;
uint8_t field6;
};
// Reflection macro usage
BEGIN_ATTRIBUTES_FOR(TestStruct)
DEFINE_MEMBER(field1);
DEFINE_MEMBER(field2);
DEFINE_MEMBER(field3);
DEFINE_MEMBER(field4);
DEFINE_MEMBER(field5);
DEFINE_MEMBER(field6);
END_ATTRIBUTES
//展开TestStruct
template<>
Class* GetClass<TestStruct>() {
using ClassType = TestStruct;//ClassType就是被反射的类
static Class localClass;
enum { BASE = 0 };
enum { field1Index = 0};
localClass.fields[field1].type = GetType<decltype(TestStruct::field1)>();
localClass.fields[field1].name = { "field1" };
localClass.fields[field1].offset = offsetof(TestStruct, field1);
enum { filed2Index = 1};
localClass.fields[field2].type = GetType<decltype(TestStruct::field2)>();//成员的type
localClass.fields[field2].name = { "field2" };
localClass.fields[field2].offset = offsetof(TestStruct, field2);
TestStruct成功地定义了一个模板特例化,对于一个函数。
目前,我们需要去创建另一个模板函数,可以使用我们创建的数据,去创建一个JSON字符串。为了这样,对于任何的类型,我们可以制作一些通用的语句,这些语句需要offsetof()宏的结果去寻找在内存中的位置,当数据被存储的时候。
template<typename T>
std::string SerializeObject(T& arg) {
const Class* objectInfo = GetClass<T>();//获取相关的Class类
rapidjson::Document document;
rapidjson::Value key;
rapidjson::Value value;
document.SetObject();
for (const auto& field : objectInfo->fields) {//遍历字段
if (field.type == nullptr) break;
key.SetString(field.name.c_str(), field.name.size(), document.GetAllocator());//成员变量名
int8_t* source = reinterpret_cast<int8_t*>(&arg) + field.offset;
//获取变量在内存中的位置
switch (field.type->enumName) {//遍历类型
case TypeName::int8_t:
case TypeName::int16_t:
case TypeName::int32_t: {
int32_t destination = 0;
memcpy(&destination, source, field.type->size);
value.SetInt(destination);
break;
}
case TypeName::uint8_t:
case TypeName::uint16_t:
case TypeName::uint32_t: {
uint32_t destination = 0;
memcpy(&destination, source, field.type->size);
value.SetUint(destination);
break;
}
default:
assert(false);
break;
}
document.AddMember(key, value, document.GetAllocator());
}
//写入到buffer里面
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
document.Accept(writer);
return buffer.GetString();
}
为了去访问我们的数据,我们必须找到在内存中我们数据确切的位置。
int8_t* source = reinterpret_cast<int8_t*>(&arg) + field.offset;
reinterpret_cast总是被转换成1字节的类型。
((base address + offsetof()))代替(base address + sizeof(arg) + offsetof())
Deserialization
对于反序列化,过程是有些不同,但是想法是一致的。
相对于返回一个字符串,我们返回一个以JSON字符串作为函数参数的反射类型。
template<typename T>
T DeserializeObject(const std::string& json) {
const Class* objectInfo = GetClass<T>();
T result;
rapidjson::Document document;
document.Parse(json.c_str());
for (const auto& field : objectInfo->fields) {
if (field.type == nullptr) break;
if (document.HasMember(field.name.c_str()) &&
(document[field.name.c_str()].IsInt() || document[field.name.c_str()].IsUint())) {
auto* destination = reinterpret_cast<int8_t*>(&result) + field.offset;
auto source = document[field.name.c_str()].GetInt();
memcpy(destination, &source, field.type->size);
}
}
return result;
}
浙公网安备 33010602011771号