当前位置: 首页 > article >正文

通用序列化和反序列化实现思路

本文简单的记录一下采用模板来实现序列化与反序列的思路, 同时采用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;
}



http://www.kler.cn/news/363369.html

相关文章:

  • [DB] Database Compression
  • grafana failed to load dashboard from file= ... json error=EOF
  • vue elementui el-table实现增加行,行内编辑修改
  • oracle和hive之间关于sql的语法差异及转换
  • 安全边际篇
  • 基于AI识别数据的Vue.js图像框选标注
  • 使用注解@ExcelIgnoreUnannotated实现了在导出 Excel 时忽略没有被标注的字段
  • 【已解决】【Hadoop】HDFS操作时:未找到命令的解决办法
  • 无线领夹麦克风哪个牌子好,麦克风品牌排行榜前十名,选购推荐
  • 人工智能:重塑未来生活与工作的科技力量
  • QT 实现随机码验证
  • Spring + WebSocket
  • SparkSQL整合Hive
  • Java集合(2 :List)
  • 【数据库】postgres数据库命令
  • 【树莓派 5B】Python 版本切换
  • C++之《剑指offer》学习记录(6):unordered_set和unordered_map
  • Proteus8使用教程
  • 如何使用pycharm测试自己的后端接口
  • 使用.NET MAUI开发第一个安卓APP
  • Fine-tuning 和 LoRA 和 QLoRA的区别
  • 常用于OBD系统的单端K总线收发器芯片资料:CSM9241
  • 【学习笔记】RFID
  • Facebook网页版登录不了是什么原因?如何解决?
  • Jtti:服务器GPU占用率过高是好事还是坏事?
  • 数字三角形模型