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

news/2024/7/10 19:32:49 标签: c++, 开源, qt

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

文章目录

  • 一:引言
  • 二、cereal简介
  • 三、cereal的下载和使用

一:引言

序列化 (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在大小和时间上都有不错的表现(其实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文件夹拷贝到指定位置,并设置系统环境变量为头文件所在路径,如:“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


http://www.niftyadmin.cn/n/5072127.html

相关文章

Linux TCP协议通信 (流程 三次握手 四次挥手 滑动窗口)

TCP通信流程 Socket函数 TCP通信实现&#xff08;服务器端&#xff09; #include <stdio.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <stdlib.h> int main() {//1.创建socketint lfd socket(AF_INET, SOCK_…

Mysql——创建数据库,对表的创建及字段定义、数据录入、字段增加及删除、重命名表。

一.创建数据库 create database db_classics default charsetutf8mb4;//创建数据库 use db_classics;//使用该数据库二.对表的创建及字段定义 create table if not exists t_hero ( id int primary key auto_increment, Name varchar(100) not null unique, Nickname varchar(1…

Kaggle - LLM Science Exam(二):Open Book QAdebertav3-large详解

文章目录 前言&#xff1a;优秀notebook介绍三、Open Book Q&A3.1 概述3.2 安装依赖&#xff0c;导入数据3.3 数据预处理3.3.1 处理prompt3.3.2 处理wiki数据 3.4 使用faiss搜索获取匹配的Prompt-Sentence Pairs3.5 查看context结果并保存3.6 推理3.6.1 加载测试集3.6.2 定…

功率谱密度估计 - welch方法的实现

因本人知识欠缺&#xff0c;后续再对下述展开讲述。 clc;clear;close all; fs 44100; t 0:1/fs:1-1/fs; x randn(size(t));load("myfir64.mat"); filtercoe myfir64; y filter(filtercoe, 1, x);[Hx, w] freqz(filtercoe, 1, fs); fx w*fs/2/pi; subplot(211…

0.Linux环境搭建

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 Linux环境搭建方式&#xff1a; 1.双系统&#xff0c;也就是说&#xff0c;假如你的电脑系统是Windows&#xff0c;那就再安上一个Linux系统&#xff0c;这种方式非常不推荐&#xff0c;一个不小心你原来的系统也挂了&#…

群晖搭建docker系统和办公服务2

首先先确认下我们的Office是否为VOL版&#xff0c;方法如下&#xff08;请您根据自身情况更改以下命令&#xff09;&#xff1a; 管理员身份运行命令提示符&#xff0c;输入 cd C:\Program Files\Microsoft Office\Office16 切换目录 &#xff08;这里请根据您自己的Office版本…

c++视觉ROI 区域和ROI 区域图像叠加

ROI 区域提取和ROI 区域图像叠加 ROI 区域提取 #include <opencv2/opencv.hpp>int main() {// 读取图像cv::Mat image cv::imread("1.jpg");// 检查图像是否成功加载if (image.empty()) {std::cerr << "Error: Could not read the image." …

EV证书与OV证书的区别

在保护网站和用户数据的过程中&#xff0c;选择适当的SSL证书至关重要。EV&#xff08;Extended Validation&#xff09;证书和OV&#xff08;Organization Validation&#xff09;证书是SSL证书的两种常见类型&#xff0c;它们在验证过程和信任指示方面有着显著的区别。让我们…