qt 保存富文本到数据_干货分享 | Pupping:一种序列化数据的方法(上)

作者:Daniel Randle

本文节选翻译自《Pupping - a method for serializing data》

本期内容文字量爆炸,请慢慢阅读~

Pupping:一种序列化数据的方法(上)

aba756c4b9a620d18189be4c3343b46c.png

声明

本文介绍的这种序列化方法并非作者原创,它属于阿拉斯加大学费尔班克斯分校的模拟课程教授Orion Lawlor。作者在听课之后,采用了教授的方法进行了创作。

介绍

序列化是将对象的状态信息转换为可以存储或传输的形式的过程。有很多方法可以做到这一点,本文将讨论一种叫pupping的模式或方法,它与boost序列化库使用的方法非常相似。

包装和拆包

Pup代表打包解包(pack-unpack),因此puping是打包、解包(packing/unpacking)的意思。这个想法是这样的:与其为每种对象类型或每种类型的媒体(文件、网络、图形用户界面中的小部件等)创建序列化/反序列化函数,不如为每种类型的媒体创建pupper对象。pupper对象包含处理对特定介质的读/写所需的数据和函数。例如,二进制文件pupper可能包含每个pup函数用于从/到读/写的文件流。

说明

Puping这个模式与boost序列化非常相似,理解并学会运用这种模式就不需要再增加对boost的依赖性。“Pupper”在某种程度上等同于boost存档,而pup函数等同于boost序列化函数。

这里给出的代码比boost更简单,并且不会使运算符超载。它是非侵入性的,而且模型简单。即任何对象,无论多么复杂,都可以通过递归分解对象直到达到基本数据类型来序列化为字节流。任何基本数据类型都可以直接用字节表示。保存/加载/传输原始数据的过程可与序列化对象分开。然后,只需要编写代码来序列化对象,任何事情都可以用原始数据完成。

Pupper模式在几个方面与其他序列化方法不同:

1)除了在最低级别(pupper对象)中的读写操作之外不分开

2)需要序列化的对象不需要从任何特殊类继承(inherit)

3)开销小,不使用外部库,同时保持可扩展性和灵活性

4)如果需要多态序列化,则基类中需要一个虚方法。CRTP可用于辅助此过程。

e8ebc1b54d3728609242005b8425fe81.png

不是在每个对象中创建一个类方法来提供序列化,而是为每个对象创建一个全局函数。除了应该序列化的对象的参数之外,所有全局函数都应具有相同的名称和参数。任何对象“可序列化”只是编写全局函数的问题。

stl容器(stl containers)的pop原型的一些示例如下所示。

template

pup(pupper * p, std::map & map, const var_info & info);

template

pup(pupper * p, std::vector & vec, const var_info & info);

template

pup(pupper * p, std::set & set, const var_info & info);

e8ebc1b54d3728609242005b8425fe81.png

稍后将解释pupper指针和var_info的参数。重要的是序列化工作是在全局函数中完成的,而不是在成员函数中完成的。通过示例最简单地示出了PUP模式pup pattern。

在本文中,对二进制文件和文本文件保存/加载的pupper对象进行编码,并使用它们保存/加载一些示例对象。给出了使用pupper模式和CRTP来序列化多态对象的示例。此外,保存/加载了多态基础对象的std :: vector,说明了此模式在使用其他库定义类型(std :: vector)时允许的灵活性。所以不用多说,看看pupper头文件。

本文对二进制和文本文件保存/加载的pupper对象进行了编码,并使用它们保存/加载了一些例子。这里给出了一个使用pupper模式和CRTP序列化多态对象的示例。另外,一个多态基对象的std::vector 被保存/加载,以此说明这种模式使用其他库定义的类型(std::vector)的时候的灵活性。因此,不需要更加麻烦的操作,现在就来看看pupper文件的开头header file

#define PUP_OUT 1

#define PUP_IN 2

#include

#include

#include

struct var_info

{

    var_info(const std::string & name_):

    name(name_)

    {}

    virtual ~var_info() {}

    std::string name;

};

struct pupper

{

    pupper(int32_t io_):

    io(io_)

    {}

    virtual ~pupper() {}

    virtual void pup(char & val_, const var_info & info_) = 0;

    virtual void pup(wchar_t & val_, const var_info & info_) = 0;

    virtual void pup(int8_t & val_, const var_info & info_) = 0;

    virtual void pup(int16_t & val_, const var_info & info_) = 0;

    virtual void pup(int32_t & val_, const var_info & info_) = 0;

    virtual void pup(int64_t & val_, const var_info & info_) = 0;

    virtual void pup(uint8_t & val_, const var_info & info_) = 0;

    virtual void pup(uint16_t & val_, const var_info & info_) = 0;

    virtual void pup(uint32_t & val_, const var_info & info_) = 0;

    virtual void pup(uint64_t & val_, const var_info & info_) = 0;

    virtual void pup(float & val_, const var_info & info_) = 0;

    virtual void pup(double & val_, const var_info & info_) = 0;

    virtual void pup(long double & val_, const var_info & info_) = 0;

    virtual void pup(bool & val_, const var_info & info_) = 0;

    int32_t io;

};

void pup(pupper * p, char & val_, const var_info & info_);

void pup(pupper * p, wchar_t & val_, const var_info & info_);

void pup(pupper * p, int8_t & val_, const var_info & info_);

void pup(pupper * p, int16_t & val_, const var_info & info_);

void pup(pupper * p, int32_t & val_, const var_info & info_);

void pup(pupper * p, int64_t & val_, const var_info & info_);

void pup(pupper * p, uint8_t & val_, const var_info & info_);

void pup(pupper * p, uint16_t & val_, const var_info & info_);

void pup(pupper * p, uint32_t & val_, const var_info & info_);

void pup(pupper * p, uint64_t & val_, const var_info & info_);

void pup(pupper * p, float & val_, const var_info & info_);

void pup(pupper * p, double & val_, const var_info & info_);

void pup(pupper * p, long double & val_, const var_info & info_);

void pup(pupper * p, bool & val_, const var_info & info_);

~向下滑动~

e8ebc1b54d3728609242005b8425fe81.png

首先声明一个var-info结构,它现在只具有一个名称字段(name field)——这是关于pupped变量的信息所属的地方。它在pupping过程中填写,因此会创建一个需要字段信息的构造函数,这样以后就不会忘记它了。pupper基类定义了任何类型的pupper必须执行的一组方法,一种处理从/到媒体的每个基本数据类型的读/写的方法。一组名为“pup”的全局函数被定义并且被宣布了,同时还建立了pupping模式的基本用法。

其思想是能够在代码的任何地方调用pup(pupper、object、description),以便序列化/反序列化任何可序列化的对象。创建新的pupper对象类型包括对每个基本数据类型执行pup方法。这些方法随后由pup全局函数使用,而pup函数又用于更复杂的类型。无论创建了多少新的pupper类型,序列化每个对象的pup函数只需要编写一次。这正是使这种模式有用的原因。

要使所有对象都可序列化为二进制文件/文本文件/Qt对话框(Qt dialog),首先要创建二进制文件/文本文件/Qt对话框 pupper。某些类型的pupper对象可能需要有关变量的其他信息。例如,有多种方法可以在图形用户界面中表示双精度数,例如垂直滑块、水平滑块、数字显示框等。

var_info结构允许添加有关变量的新信息。任何不需要该信息的pupper对象都可以忽略它。使用Qt作为例子,可以将标志(flag)添加到var_info结构并由Qt pupper对象使用。然后,需要在图形用户界面中显示的对象需要设置标志,所有不使用该标志的pupper对象都会忽略它。通过将var_info的析构函数变为虚函数,可以扩展var_info结构。如果创建其他人将使用的库,这也很有用。它允许用户创建自己的pupper对象类型,并将任何必要的数据添加到var_info,而无需编辑库源代码。

使用pup(pupper,object,description)而不是pupper-> pup(object,description)或object-> pup(pupper,description)有几个原因。

不使用pupper-> pup(对象,描述)的原因:

1)必须为每种新类型的对象扩展基础pupper种类。如果使用可扩展类创建库,则库的用户必须为扩展的每个类编辑基本pupper种类,其中库仍负责序列化。

2)打包/解包代码将与对象分离,使其在对对象进行更改时容易出错。

不使用object-> pup(pupper,description)的原因:

1)不能轻易地扩展第三方库对象(如std::vector)来包含pup函数,它们需要一个特殊的函数或包装类(wrapper class)

2)因为许多对象不包含“pup”函数,所以会与pup的用法不一致。

e8ebc1b54d3728609242005b8425fe81.png

虽然这纯粹是一个关于美学和便利的争论,当然这也是一种观点。但我认为应该这样写:

pup(pupper,obj1,desc1);

pup(pupper,obj2,desc2);

pup(pupper,obj3,desc3);

pup(pupper,obj4,desc4);

//etc...

是不是比下面这个更好理解和记忆。

obj1->pup(pupper,desc1);

pup(pupper,obj2,desc2);

obj3->pup(pupper,desc3);

pup(pupper,obj4,desc4);

//etc...

e8ebc1b54d3728609242005b8425fe81.png

如果所有内容都使用相同的pup函数格式,那么编写pup函数就变得很简单,因为它们只是相同格式的不同pup函数的组合。创建具体的pupper对象很容易——以二进制和文本文件pupper对象作为例子。它们的定义代码很无聊,所以这里不会为大家展示,但是其声明(declarations)如下。

//binary_file_pupper header

#include "pupper.h"

struct binary_file_pupper : public pupper

{

    binary_file_pupper(std::fstream & fstrm, int mode);

    std::fstream & fs;

    void pup(char & val_, const var_info & info_);

        void pup(wchar_t & val_, const var_info & info_);

        void pup(int8_t & val_, const var_info & info_);

        void pup(int16_t & val_, const var_info & info_);

        void pup(int32_t & val_, const var_info & info_);

    void pup(int64_t & val_, const var_info & info_);

        void pup(uint8_t & val_, const var_info & info_);

        void pup(uint16_t & val_, const var_info & info_);

        void pup(uint32_t & val_, const var_info & info_);

        void pup(uint64_t & val_, const var_info & info_);

        void pup(float & val_, const var_info & info_);

        void pup(double & val_, const var_info & info_);

        void pup(long double & val_, const var_info & info_);

        void pup(bool & val_, const var_info & info_);

};

template

void pup_bytes(binary_file_pupper * p, T & val_)

{

    if (p->io == PUP_IN)

        p->fs.read((char*)&val_, sizeof(T));

    else

        p->fs.write((char*)&val_, sizeof(T));

}

//text_file_pupper header

#include "pupper.h"

struct text_file_pupper : public pupper

{

    text_file_pupper(std::fstream & fstrm, int mode);

    std::fstream & fs;

    void pup(char & val_, const var_info & info_);

    void pup(wchar_t & val_, const var_info & info_);

        void pup(int8_t & val_, const var_info & info_);

        void pup(int16_t & val_, const var_info & info_);

        void pup(int32_t & val_, const var_info & info_);

        void pup(int64_t & val_, const var_info & info_);

        void pup(uint8_t & val_, const var_info & info_);

        void pup(uint16_t & val_, const var_info & info_);

        void pup(uint32_t & val_, const var_info & info_);

        void pup(uint64_t & val_, const var_info & info_);

        void pup(float & val_, const var_info & info_);

        void pup(double & val_, const var_info & info_);

        void pup(long double & val_, const var_info & info_);

        void pup(bool & val_, const var_info & info_);

};

template

void pup_text(text_file_pupper * p, T val, const var_info & info, std::string & line)

{

    std::string begtag, endtag;

    begtag = " + info.name + ">"; endtag = "";

    if (p->io == PUP_OUT)

    {

        p->fs << begtag << val << endtag << "\n";

    }

    else

    {

        std::getline(p->fs, line);

        size_t beg = begtag.size(); size_t loc = line.find(endtag);

        line = line.substr(beg, loc - beg);

    }

}

~向下滑动~

未完待续

声明:本公众号文章,禁止转载。违者必究!

6d36f982ac4d43910fc18a626a30058c.png

如果你有一肚子干货,还有满满的表达欲望

请火速联系小编,小编将水陆空全力支持你!

联系邮箱:menglong@kingsoft.com

a1a9225bff118f99ec0ae4d51f6916a6.png fa78274b8e3a795f49687a6dc91731a1.png

长按指纹

一键关注

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值