c++序列化库cereal使用介绍

由来

一直以来没体会到序列化的好处,最近写了配置文件读写的类,搜索过程中发现用序列化的话可以很方便的存取,几行代码就能完成。

几种序列化库

C++上序列化的库没有c#或者java那么多,比较普遍的有2个:

1.google的protobuf库
2.boost的serialization库

它们都有各自的优势,但针对小型系统的配置文件存储的话都太大太笨重了,特别是boost的serialization库居然还要编译,几百M的库,编译要2个小时,不划算。
在github上搜索到一个cereal库,获得里高达715个star,而且虽然它不支持wstring宽格式字符,但可以使用中文作为string变量,因此是可以完美支持中文配置文件的。这里记录下它的使用。

cereal库使用

平台

注意cereal基于c++11,需要支持c++11的编译器。

下载解压

在文献3或者4里下载到压缩包,将里面的include文件夹解压缩出来,其他的不需要。

工程配置

新建win32控制台工程,将include文件夹放入工程目录里,并在项目-属性-c++引用目录里添加$(ProjectDir)include

代码

1.先来最简单的将基本数据类型保存为json文件。
引用库文件

#include <fstream>
#include <iostream>
#include <string>

#include <cereal/archives/JSON.hpp>
#include <cereal/types/vector.hpp>
#include <cereal/types/string.hpp>

然后在main里写

std::ofstream file("out.json");
	cereal::JSONOutputArchive archive(file);
	std::string s[] = { "this is a string", " 中文string也是可以支持的" };
	std::vector<double> vec = { 1.00001, 2e3, 30.1, -4, 5 };
	archive(CEREAL_NVP(vec), CEREAL_NVP(s));

运行成功后结果为一个标准json文件
这里写图片描述

2.再来最简单的读取json文件到变量。
main里写

std::ifstream file("out.json");
	cereal::JSONInputArchive archive(file);
	std::string s[2];
	archive(CEREAL_NVP(s));
	std::cout << s[1];

结果很完美,对中文字符和浮点数支持都很好,不愧是715个star的项目
这里写图片描述

3.稍微复杂点的自定义类写入json
定义一个结构并添加模板函数

struct Data
{
	int index = 1;
	double d[3] = { 1.0,2.33e-5,1000000.0 };
	std::vector<std::string> vs;

	template <class Archive>
	void serialize(Archive & ar)
	{
		ar(index, d, vs);
	}
};

main

	Data mydata;
	std::string s1 = "中文字符串/*-+!@#$%^&";
	std::string s2 = "english/*-+!@#$%^&";
	mydata.vs.push_back(s1);
	mydata.vs.push_back(s2);
	std::ofstream file("out.json");
	cereal::JSONOutputArchive archive(file);
	archive(CEREAL_NVP(mydata));

结果同样符合预期
这里写图片描述
注意这里的项目-值对里的项目都是默认的value0-value2,并不是结构的变量名,如果想在json文件里保存到变量名,可以用下面的方法

archive(CEREAL_NVP(mydata.index), CEREAL_NVP(mydata.d), CEREAL_NVP(mydata.vs));

这样结果便是理想的形式了
这里写图片描述

4.稍微复杂点的从json读入自定义类的变量
和基本数据类型读取archive(CEREAL_NVP(s));
不一样了,如果这样写,那么cereal将会抛出一个json异常。因为NVP找不到该变量了。要用另外一个cereal的模板cereal::make_nvp

	std::ifstream file("out.json");
	cereal::JSONInputArchive archive(file);
	int n;
	std::vector<std::string> s;
	double d[3];
	archive(cereal::make_nvp("mydata.index", n));
	archive(cereal::make_nvp("mydata.d", d));
	archive(cereal::make_nvp("mydata.vs", s));
	std::cout << s[1] << '\t' << s[0] << '\t' << d[1]; 

便能正确读入这些变量了。
注意,保存的格式要为archive(CEREAL_NVP(mydata.index), CEREAL_NVP(mydata.d), CEREAL_NVP(mydata.vs));时上面的代码才能正确读取。尝试了几次,按照archive(CEREAL_NVP(mydata));反序列化时都会抛出异常,如果谁知道怎么正确读取请告诉我

4.嵌套的自定义类写入json

假设有如下结构

struct Dat
{
	int x;
	int y;
};


struct Data
{
	Dat dat = { 2,3 };
	int index = 1;
	double d[3] = { 1.0,2.33e-5,1000000.0 };
  	std::vector<std::string> vs;
};

想在保存的文件里,完整的存贮结构变量名,怎么办呢,方法如下:

struct Dat
{
	int x;
	int y;
	template <class Archive>
	void serialize(Archive & ar)
	{
		ar(cereal::make_nvp("x", x), cereal::make_nvp("y", y));
	}
};


struct Data
{
	Dat dat = { 2,3 };
	int index = 1;
	double d[3] = { 1.0,2.33e-5,1000000.0 };
	std::vector<std::string> vs;
	template <class Archive>
	void serialize(Archive & ar)
	{
		ar(cereal::make_nvp("Dat", dat), cereal::make_nvp("index", index), cereal::make_nvp("d", d), cereal::make_nvp("vs", vs));
	}
	
};

序列化保存的时候

Data mydata;
	mydata.dat.x = 50;
	mydata.dat.y = 150;
	std::string s1 = "中文字符串/*-+!@#$%^&";
	std::string s2 = "english/*-+!@#$%^&";
	mydata.vs.push_back(s1);
	mydata.vs.push_back(s2);
	

	std::ofstream file("out.json");
	cereal::JSONOutputArchive archive(file);
	archive(cereal::make_nvp("mydata", mydata));

反序列化读取的时候

std::ifstream file("out.json");
	cereal::JSONInputArchive archive(file);
	Data mydata;
	archive(cereal::make_nvp("mydata", mydata));
//要修改某个配置的时候
	mydata.index = 45;
//再序列化回去
	std::ofstream file2("out.json");
	cereal::JSONOutputArchive archive2(file2);
	archive2(cereal::make_nvp("mydata", mydata));

结果如图:

这里写图片描述

自定义类的序列化

cereal虽然很好,但只支持STL的类,在Qt里不支持Qt下的很多类,比如QVector、QList、QString。其实很好处理,重载一下就可以,上代码

namespace cereal {
template<class Archive, class c> inline
void save(Archive &ar, QVector<c> const &v)//QVector序列化
{
    for (int i = 0; i < v.size(); i++)
        ar(v.at(i));
}
template<class Archive, class c> inline
void load(Archive &ar, QVector<c> &v)
{
    v.clear();
    while (true)
    {
        const auto namePtr = ar.getNodeName();

        if (!namePtr)
            break;
        int value;
        ar(value);
        v.push_back(value);
    }
}

template<class Archive> inline
void save(Archive &ar, QString const &s)//QString序列化
{
    ar(cereal::make_nvp("str", s.toStdString()));
}

template<class Archive> inline
void load(Archive &ar, QString &s)
{
    s.clear();
    while (true)
    {
        const auto ptr = ar.getNodeName();
        if (!ptr)
            break;
        std::string value;
        ar(value);
        s = QString::fromStdString(value);
    }
}
}

使用的时候就可以和其他std库的类一样用了,这里特别提一下中文变量

std::ofstream file("out.json");

    {
        cereal::JSONOutputArchive::Options opts(3);
        cereal::JSONOutputArchive oarchive(file, opts); // Create an output archive

        MyClass m1(1, 33.322f, QStringLiteral("sdfef中文"), 234);
        m1.va.push_back(222);
        m1.va.push_back(33);
        oarchive(CEREAL_NVP(m1));
    }

    std::ifstream file2("out.json");
    {
        cereal::JSONInputArchive iarchive(file2); // Create an output archive

        MyClass m2;

        iarchive(m2);
        qDebug()<<m2.x<<m2.y<<m2.str<<m2.data.a<<m2.va.first();
    }

结果含 有正确的中文。

  • 注意,仍然不支持变量名是中文

参考文献

  1. 使用C++进行对象序列化
  2. 最常用的两种C++序列化方案的使用心得(protobuf和boost serialization
  3. cereal
  4. cereal quickstart
  • 13
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值