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