本文简单的记录一下采用模板来实现序列化与反序列的思路, 同时采用C++20标准的concept和requires来简化模板函数的选择。
首先了解一下自定义类支持序列化的两种方式:
一、序列化自定义类型(侵入式)
struct Test {
std::string name;
int age;
//序列化接口
template<class Archive>
void serialize(Archive & ar) const {
ar & REFLEX(name);
ar & REFLEX(age);
}
};
二、序列化自定义类型(非侵入式)
struct Test2 {
std::string name;
int age;
};
//序列化接口
template<class Archive>
void serialize(Archive & ar,const Test2& t) {
ar & REFLEX(t.name);
ar & REFLEX(t.age);
}
这种方法用于序列化一些外部库定义的类,或一些不希望修改实现的类。
接下来实现一个采用二进制方案的序列化类
//用于识别自定义类内部是否支持serialize函数
template<typename AR, typename V>
concept is_user_def_inside = requires(AR ar, V v) {
v.serialize(ar);
};
//用于识别自定义类外部是否支持了serialize函数
template<typename AR, typename V>
concept is_user_def_outside = requires(AR ar, V v) {
serialize(ar, v);
};
class ArchiveOut {
public:
ArchiveOut(std::ostream& os):m_os(os){
}
using ArchiveTp = ArchiveValue;
template<typename T>
ArchiveOut& operator & (const T& val){
this->operator<<(val);
return *this;
}
//自定义类(侵入式)
template<typename T>
requires(is_user_def_inside<ArchiveOut,T>)
ArchiveOut& operator << (const T& val){
val.serialize(*this);
return *this;
}
// 可平凡复制
template<typename T>
requires(std::is_trivially_copyable<T>::value)
ArchiveOut& operator << (const T& val){
m_os.write((const char *)&val, sizeof(T));
return *this;
}
//自定义类(非侵入式)
template<typename T>
requires(!std::is_trivially_copyable<T>::value && !is_user_def_inside<ArchiveOut,T> && is_user_def_outside<ArchiveOut,T>)
ArchiveOut& operator << (const T& val){
serialize(*this, val);
return *this;
}
//string 特化
ArchiveOut& operator << (const std::string& val){
size_t size = val.size();
m_os.write((const char *)&size, sizeof(size));
m_os.write((const char *)val.data(), size * sizeof(typename std::string::value_type));
return *this;
}
//其它类型处理
private:
std::ostream& m_os;
};
前文中的REFLEX为自定义宏, 用于生成诸如json、xml时,对字段名的反射, 因为基于二进制序列化的时候,可以只保存值,而不需要保存字段名,但生成json、xml等格式时需要用到字段名称,因此实现Reflex时,需要根据序列化类型字段选择。
#define REFLEX(param) CReflex(param, #param)
#define REFLEX_ALIAS(param, alias) CReflex(param, alias)
//只针对值进行序列化
enum class ArchiveValue {
};
//对字段名和值进行序列化
enum class ArchiveKeyValue {
};
template<typename T>
concept is_key_value = requires() {
std::is_same<typename T::ArchiveTp, ArchiveKeyValue>::value;
};
template<typename T>
concept is_only_value = requires() {
std::is_same<typename T::ArchiveTp, ArchiveValue>::value;
};
template<typename T>
class CReflex {
public:
CReflex(T& value, const std::string& strName) :m_value(value), m_name(strName) {};
template<typename Archive>
requires(is_only_value<Archive>)
void serialize(Archive& ar)const {
ar & m_value;
}
template<typename Archive>
requires(!is_only_value<Archive> && is_key_value<Archive>)
void serialize(Archive& ar)const {
ar & (m_name, m_value);
}
private:
T& m_value;
std::string m_name;
};
到这里一个大致的模型已经实现,当然,真正实施起来还有许多细节需要补充。
附完整代码:
#include <string>
#include <concepts>
#include <iostream>
#include <sstream>
#include <type_traits>
#define REFLEX(param) CReflex(param, #param)
#define REFLEX_ALIAS(param, alias) CReflex(param, alias)
//只针对值进行序列化
enum class ArchiveValue {
};
//对字段名和值进行序列化
enum class ArchiveKeyValue {
};
template<typename T>
concept is_key_value = requires() {
std::is_same<typename T::ArchiveTp, ArchiveKeyValue>::value;
};
template<typename T>
concept is_only_value = requires() {
std::is_same<typename T::ArchiveTp, ArchiveValue>::value;
};
template<typename T>
class CReflex {
public:
CReflex(T& value, const std::string& strName) :m_value(value), m_name(strName) {};
template<typename Archive>
requires(is_only_value<Archive>)
void serialize(Archive& ar)const {
ar & m_value;
}
template<typename Archive>
requires(!is_only_value<Archive> && is_key_value<Archive>)
void serialize(Archive& ar)const {
ar & (m_name, m_value);
}
private:
T& m_value;
std::string m_name;
};
template<typename T>
concept is_container = requires(T res, T::value_type v) {
res.insert(res.begin(), v);
};
template<typename AR, typename V>
concept is_user_def_inside = requires(AR ar, V v) {
v.serialize(ar);
};
template<typename AR, typename V>
concept is_user_def_outside = requires(AR ar, V v) {
serialize(ar, v);
};
class ArchiveOut {
public:
ArchiveOut(std::ostream& os):m_os(os){
}
using ArchiveTp = ArchiveValue;
template<typename T>
ArchiveOut& operator & (const T& val){
this->operator<<(val);
return *this;
}
//自定义类(侵入式)
template<typename T>
requires(is_user_def_inside<ArchiveOut,T>)
ArchiveOut& operator << (const T& val){
val.serialize(*this);
return *this;
}
// 可平凡复制
template<typename T>
requires(std::is_trivially_copyable<T>::value)
ArchiveOut& operator << (const T& val){
m_os.write((const char *)&val, sizeof(T));
return *this;
}
//自定义类(非侵入式)
template<typename T>
requires(!std::is_trivially_copyable<T>::value && !is_user_def_inside<ArchiveOut,T> && is_user_def_outside<ArchiveOut,T>)
ArchiveOut& operator << (const T& val){
serialize(*this, val);
return *this;
}
//string 特化
ArchiveOut& operator << (const std::string& val){
size_t size = val.size();
m_os.write((const char *)&size, sizeof(size));
m_os.write((const char *)val.data(), size * sizeof(typename std::string::value_type));
return *this;
}
//其它类型处理
private:
std::ostream& m_os;
};
struct Test {
std::string name;
int age;
//序列化接口
template<class Archive>
void serialize(Archive & ar) const {
ar & REFLEX(name);
ar & REFLEX(age);
}
};
struct Test2 {
std::string name;
int age;
};
//序列化接口
template<class Archive>
void serialize(Archive & ar,const Test2& t) {
ar & REFLEX(t.name);
ar & REFLEX(t.age);
}
int main()
{
Test t = {"zhangshan", 36};
Test2 t2 = {"liubei", 38};
std::ostringstream ss;
ArchiveOut ar(ss);
ar << t << t2;
std::cout << "size : " << ss.str().length() << ", value: " << ss.str() << std::endl;
return 0;
}