cereal:支持C++11的开源序列化库

这篇具有很好参考价值的文章主要介绍了cereal:支持C++11的开源序列化库。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

cereal:支持C++11的开源序列化库

一:引言

序列化 (Serialization)

程序员在编写应用程序的时候往往需要将程序的某些数据存储在内存中,然后将其写入某个文件或是将它传输到网络中的另一台计算机上以实现通讯。这个将程序数据转化成能被存储并传输的格式的过程被称为“序列化”(Serialization),而它的逆过程则可被称为“反序列化” (Deserialization)。

值得推荐的开源C/C++框架和库:https://www.cnblogs.com/lidabo/p/5514155.html

二、cereal简介

cereal是一个开源的(BSD License)、轻量级的、支持C++11特性的、仅仅包含头文件实现的、跨平台的C++序列化库。它可以将任意的数据类型序列化成不同的表现形式,比如二进制、XML格式或JSON。cereal的设计目标是快速、轻量级、易扩展——它没有外部的依赖关系,而且可以很容易的和其他代码封装在一块或者单独使用。

cereal支持标准库的几乎每一个类型的序列化。cereal也完全支持继承和多态。由于cereal被设计为一个精简、快速的库,它不像其他序列化库(比如Boost)在同一层次上会进行对象跟踪,这也导致了它不支持原始指针(raw pointer)和引用,但是智能指针(比如std::shared_ptr和std::unique_ptr)是没有问题的。

cereal适用于基于C++11标准的各种编译器

cereal使用了一些C++11的新特性,因此需要一个兼容性更好的的C++编译器才能正常工作。已被验证可用的编译器有g++4.7.3、clang++3.3、MSVC2013,或者更新版本。

它也可能可以在老版本编译器上工作,但并不保证完全支持。当使用g++或clang++编译器时,cereal同时需要libstdc++和libc++库。

cereal:更快速,更好的压缩
在简单的性能测试中,cereal通常比Boost的序列化库速度更快,而且产生的二进制形式占用更少的空间,尤其是针对更小的对象。cereal使用了C++中的速度最快的XML和JSON解析器和包装器。

cpp-serializers 这个项目中,作者对比了目前常见的序列化库:
cereal库,c++,开源,qt

可视化的模型对比
cereal库,c++,开源,qt

平均时间
cereal库,c++,开源,qt

可以看到,cereal在大小和时间上都有不错的表现(其实yas的速度好像更快,回头评测一下。。)

cereal是易于使用的

在代码增加cereal序列化功能可以简化为包含一个头文件,写一个序列化函数。无论是从概念上还是代码层次上,cereal的功能都是自文档化的。
如果你使用错误,cereal尽可能的在编译期触发静态断言。

对于Boost使用者来说,cereal提供了相似的语法,如果你使用过Boost的序列化库,你会发现cereal的语法看起来很熟悉。

如果你是从Boost转向使用cereal,一定要阅读这个过渡指南:http://uscilab.github.io/cereal/transition_from_boost.html

三、cereal的下载和使用

cereal的官方下载地址为:http://uscilab.github.io/cereal/index.html

编译源码过程很简单,在此不详述(注意源码解压在全英文路径下进行编译,不然出现编译失败的错误)。

使用cmake工具对源码进行编译和安装,选择对应版本的编译器和x86/x64选项,最终释放出一系列头文件到安装路径:


cereal库,c++,开源,qt

将cereal文件夹拷贝到指定位置,并设置系统环境变量为头文件所在路径,如:“D:\3rdLib\cereal\include”。

接下来就可以使用cereal库了~

我这里给他封装成了两个头文件:ConfigFile.h和ConfigComm.h

ConfigFile.h:

#ifndef _ConfigFile_Head_File_
#define _ConfigFile_Head_File_


#include <cereal/archives/json.hpp>
#include <fstream>
#include "ConfigComm.h"

template<typename DataType>
class ConfigFile
{
public:
	int SetConfigFile(std::string configFile)
	{
		_configFile = configFile;

		std::ifstream f(_configFile);
		if (f.good() == true)
		{
			_fileExist = true;
			return 1;
		}

		_errMsg = "配置文件 \"" + _configFile + "\" 不存在";
		return -1;
	}

	int Load()
	{
		if (_fileExist == false)
		{
			_errMsg = "配置文件 \"" + _configFile + "\" 不存在";
			return -1;
		}

		try
		{
			std::ifstream is(_configFile);
			cereal::JSONInputArchive ar(is);
			ar(_data);
		}
		catch (CerealError err)
		{
			_errMsg = "配置文件 \"" + _configFile + "\" " + err.errMsg;
			return -1;
		}
		catch (...)
		{
			_errMsg = "配置文件 \"" + _configFile + "\" 存在错误";
			return -1;
		}

		return 1;
	}

	int Save()
	{
		if (_configFile.empty() == true)
		{
			_errMsg = "配置文件路径为空";
			return -1;
		}

		try
		{
			std::ofstream os(_configFile);
			cereal::JSONOutputArchive ar(os);
			ar(cereal::make_nvp("配置信息", _data));
		}
		catch (...)
		{
			_errMsg = "配置文件 \"" + _configFile + "\" 保存失败";
			return -1;
		}

		return 1;
	}

	int Load(std::string filename)
	{
		if (SetConfigFile(filename) > 0)
			return Load();

		return -1;
	}

	int Save(std::string filename)
	{
		_configFile = filename;
		return Save();
	}

	DataType& Data()
	{
		return _data;
	}

private:
	bool _fileExist = false;
	std::string _configFile;

	DataType _data;

public:
	std::string GetLastErrorMsg() { return _errMsg; }

protected:
	std::string _errMsg;
};

struct FileListConfig
{
	std::vector<std::string> lst;

	template<class Archive>
	void serialize(Archive& archive)
	{
		CustomCereal(archive, "配置文件列表", lst);
	}
};

template<typename DataType>
class ConfigFileList
{
public:
	int SetListConfigFile(std::string listConfigFile)
	{
		_listConfigFile = listConfigFile;

		ConfigFile<FileListConfig> flc;
		if (flc.SetConfigFile(_listConfigFile) > 0)
		{
			_fileExist = true;

			if (flc.Load() > 0)
			{
				_cfList.clear();

				for (std::string filename : flc.Data().lst)
				{
					ConfigFile<DataType> configFile;
					if (configFile.SetConfigFile(filename) > 0)
					{
						_cfList.push_back(configFile);
					}
					else
					{
						_errMsg = configFile.GetLastErrorMsg();
						return -1;
					}
				}
			}
			else
			{
				_errMsg = flc.GetLastErrorMsg();
				return -1;
			}

			return 1;
		}

		_errMsg = "列表配置文件 \"" + _listConfigFile + "\" 不存在";
		return -1;
	}

	int Load()
	{
		if (_fileExist == false)
		{
			_errMsg = "列表配置文件 \"" + _listConfigFile + "\" 不存在";
			return -1;
		}

		for (ConfigFile<DataType>& configFile : _cfList)
		{
			if (configFile.Load() < 0)
			{
				_errMsg = configFile.GetLastErrorMsg();
				return -1;
			}
		}

		if (_cfList.size() == 0)
		{
			_errMsg = "列表配置文件 \"" + _listConfigFile + "\" 列表数据为空";
			return  -1;
		}

		return 1;
	}

	int Save()
	{
		if (_cfList.size() == 0)
		{
			_errMsg = "列表配置文件 \"" + _listConfigFile + "\" 保存数据为空";
			return -1;
		}

		for (ConfigFile<DataType>& configFile : _cfList)
		{
			if (configFile.Save() < 0)
			{
				_errMsg = configFile.GetLastErrorMsg();
				return -1;
			}
		}

		return 1;
	}

	int Load(std::string listConfigFile)
	{
		if (SetListConfigFile(listConfigFile) > 0)
			return Load();

		return -1;
	}

	int Save(std::string listConfigFile)
	{
		_listConfigFile = listConfigFile;	

		ConfigFile<FileListConfig> flc;
		if (flc.SetConfigFile(_listConfigFile) > 0)
		{
			if (flc.Load() > 0)
			{
				for (std::string filename : flc.Data().lst)
				{
					ConfigFile<DataType> configFile;
					if (configFile.Save(filename) < 0)
					{
						_errMsg = configFile.GetLastErrorMsg();
						return -1;
					}
				}
			}
			else
			{
				_errMsg = flc.GetLastErrorMsg();
				return -1;
			}

			return 1;
		}
		
		_errMsg = flc.GetLastErrorMsg();
		return -1;
	}

	int Size()
	{
		return _cfList.size();
	}

	DataType& operator[](int index)
	{
		return _cfList[index].Data();
	}

private:
	bool _fileExist = false;
	std::string _listConfigFile;

	std::vector<ConfigFile<DataType>> _cfList;

public:
	std::string GetLastErrorMsg() { return _errMsg; }

protected:
	std::string _errMsg;
};

#endif//_ConfigFile_Head_File_
 

ConfigComm.h

#ifndef _ConfigComm_Head_File_
#define _ConfigComm_Head_File_

#include <cereal/types/base_class.hpp>
#include <cereal/types/string.hpp>
#include <cereal/types/vector.hpp>
#include <cereal/types/array.hpp>
#include <cereal/types/map.hpp>

struct CerealError
{
	CerealError(std::string err)
	{
		errMsg = err;
	}

	std::string errMsg = "";
};

template<class Archive, typename valueType>
void CustomCereal(Archive& archive, std::string itemName, valueType& value, bool canIgnore = false)
{
	try
	{
		archive(cereal::make_nvp(itemName, value));
	}
	catch (...)
	{
		if (canIgnore == false)
		{
			std::string msg = itemName + " 错误";
			throw CerealError(msg);
		}
	}
}

template<typename Type>
static void CerealOutputFile(std::string fileName, std::string rootName, Type data)
{
	try
	{
		std::ofstream os(fileName);
		cereal::JSONOutputArchive ar(os);
		ar(cereal::make_nvp(rootName, data));
	}
	catch (...)
	{
	}
}

template<typename Type>
static Type CerealInputFile(std::string fileName, std::string rootName)
{
	Type data;
	try
	{
		std::ifstream is(fileName);
		cereal::JSONInputArchive ar(is);

		ar(cereal::make_nvp(rootName, data));
	}
	catch (...)
	{
	}

	return data;
}


template<typename Type>
static std::string CerealOutputString(Type data)
{
	std::ostringstream os;
	try
	{
		cereal::JSONOutputArchive ar(os);
		ar(CEREAL_NVP(data));
	}
	catch (...)
	{
		return "";
	}

	std::string s = os.str();
	return 	s;
}

template<typename Type>
static bool CerealInputString(std::string str, Type &data)
{
	try
	{
		std::istringstream is(str);
		cereal::JSONInputArchive ar(is);
		ar(CEREAL_NVP(data));
		return 	true;
	}
	catch (std::exception ex)
	{
		return 	false;
	}
}

class IConfigBase
{
public:
	virtual ~IConfigBase() = default;

	virtual int LoadConfig(std::string configFile) = 0;
	virtual int UpdateConfig() = 0;
	virtual int SaveConfig() = 0;

	virtual void* GetConfigData() = 0;
	virtual void* GetConfigPanel(int panelMode = 0) = 0;
};

#endif//_ConfigComm_Head_File_

创建实例Test测试:

// CerealTest.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include "ConfigFile.h"

struct NetConfig
{
	bool bWireEnable = false;
	int telePort = 12345;
	int cmdPort = 12345;

	template<class Archive>
	void serialize(Archive& archive)
	{
		CustomCereal(archive, "是否为无线网络", bWireEnable);
		CustomCereal(archive, "遥测端口", telePort);
		CustomCereal(archive, "命令端口", cmdPort);
	}
};

int main()
{
	ConfigFile<LogConfig> NetConfig;

	bool t1 = NetConfig.Load("LogConfig.json");
	bool t2 = NetConfig.Save();
	int  x = NetConfig.Data().cmdPort;

	std::cout << t1 << t2 << x;  
}

具体实例和库代码可在此下载:https://download.csdn.net/download/cao_jie_xin/88380290

参考:https://zhuanlan.zhihu.com/p/391610360
https://blog.csdn.net/qq_21950929/article/details/105745509文章来源地址https://www.toymoban.com/news/detail-727301.html

到了这里,关于cereal:支持C++11的开源序列化库的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 序列化,反序列化之实例

    介绍文章 __construct() 当一个对象创建时自动调用 __destruct() 当对象被销毁时自动调用 (php绝大多数情况下会自动调用销毁对象) __sleep() 使**用serialize()函数时触发 __wakeup 使用unserialse()**函数时会自动调用 __toString 当一个对象被当作一个字符串被调用 __call() 在对象上下文中调用不

    2024年02月14日
    浏览(44)
  • 协议,序列化,反序列化,Json

    协议究竟是什么呢?首先得知道主机之间的网络通信交互的是什么数据,像平时使用聊天APP聊天可以清楚,用户看到的不仅仅是聊天的文字,还能够看到用户的头像昵称等其他属性。也就可以证明网络通信不仅仅是交互字符串那么简单。事实上网络通信还可能会通过一个结构

    2024年02月13日
    浏览(39)
  • 【网络】协议定制+序列化/反序列化

    如果光看定义很难理解序列化的意义,那么我们可以从另一个角度来推导出什么是序列化, 那么究竟序列化的目的是什么? 其实序列化最终的目的是为了对象可以 跨平台存储,和进行网络传输 。而我们进行跨平台存储和网络传输的方式就是IO,而我们的IO支持的数据格式就是

    2024年02月08日
    浏览(41)
  • 协议定制 + Json序列化反序列化

    1.1 结构化数据 协议是一种 “约定”,socket api的接口, 在读写数据时,都是按 “字符串” 的方式来发送接收的。如果我们要传输一些\\\"结构化的数据\\\" 怎么办呢? 结构化数据: 比如我们在QQ聊天时,并不是单纯地只发送了消息本身,是把自己的头像、昵称、发送时间、消息本身

    2024年02月09日
    浏览(43)
  • Java序列化和反序列化

    目录 一、序列化和反序列化 二、Java序列化演示 三、反序列化漏洞 1、含义 ​序列化就是内存中的对象写入到IO流中,保存的格式可以是二进制或者文本内容。反序列化就是IO流还原成对象。 2、用途 (1)传输网络对象 (2)保存Session 1、序列化 java.io.ObjectOutputStream代表对象

    2023年04月25日
    浏览(37)
  • 【Linux】序列化与反序列化

    目录 前言 什么是应用层? 再谈\\\"协议\\\"  什么是序列化和反序列化 网络版计算器 整体流程实现 Sock.hpp的实现 TcpServer.hpp的实现 Protocol.hpp的实现 CalServer.cc的编写 CalClient.cc的编写 整体代码           本章是属于TCP/UDP四层模型中的第一层 应用层 相关的内容。主要介绍了序列

    2024年02月10日
    浏览(39)
  • 【Linux】序列化和反序列化

    在网络编程中,直接使用 结构体 进行数据传输会出错,因为 本质上socket无法传输结构体 ,我们只有将结构体装换为字节数组,或者是字符串格式来传输,然后对端主机收到了数据,再将其转化为结构体,这就是序列化和反序列化的过程! 序列化 (Serialization)是将对象的状态

    2024年02月10日
    浏览(40)
  • Unity-序列化和反序列化

    序列化是指把对象转换为字节序列的过程,而反序列化是指把字节序列恢复为对象的过程。序列化最主要的用途就是传递对象和保存对象。 在Unity中保存和加载、prefab、scene、Inspector窗口、实例化预制体等都使用了序列化与反序列化。 1 自定义的具有Serializable特性的非抽象、

    2024年01月24日
    浏览(54)
  • 什么是序列化和反序列化?

    JSON(JavaScript Object Notation)和XML(eXtensible Markup Language)是两种常用的数据交换格式,用于在不同系统之间传输和存储数据。 JSON是一种轻量级的数据交换格式,它使用易于理解的键值对的形式表示数据。JSON数据结构简单明了,易于读写和解析,是基于JavaScript的一种常用数据

    2024年02月09日
    浏览(54)
  • Spring Boot 序列化、反序列化

    在软件开发中,序列化和反序列化是一种将对象转换为字节流以便存储或传输的机制。序列化将对象转换为字节流,而反序列化则将字节流转换为对象。序列化和反序列化在许多应用场景中都起着重要的作用,比如在网络通信中传输对象、将对象存储到数据库中、实现分布式

    2024年02月15日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包