与序列多态类型常见的混乱 [英] Common confusions with serializing polymorphic types

查看:139
本文介绍了与序列多态类型常见的混乱的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我见过很多问题,教程和文档涉及序列化派生类的,我一直没能达到的几个问题,包括达成共识(并在以下code所示):


  • 的boost ::系列化:: base_object VS BOOST_SERIALIZATION_BASE_OBJECT_NVP

  • 归档和放大器; MDATA; VS 归档和放大器; BOOST_SERIALIZATION_NVP(MDATA);

  • BOOST_SERIALIZATION_ASSUME_ABSTRACT的用处(AbstractPoint);

  • 要求连载()在分级中的类,这并不需要序列化任何东西。

code:

 的#include<升压/存档/ text_oarchive.hpp>
#包括LT&;升压/存档/ text_iarchive.hpp>
#包括LT&;升压/系列化/ shared_ptr.hpp>
#包括LT&;升压/系列化/ base_object.hpp>#包括LT&;&的fstream GT;类AbstractPoint
{
上市:
    虚拟〜AbstractPoint(){}
    虚拟无效的doSomething()= 0;    //即使是一个抽象类,我们仍然需要这种
    模板<类TArchive>
    无效连载(TArchive&安培;归档,const的无符号整型版)
    {
        // 没做什么
    }
};//这似乎并不做任何事情
// BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);Point类:公共AbstractPoint
{
上市:
    点()=默认值;
    点(const的双倍数据):MDATA(数据){}    无效DoSomething的(){}    模板<类TArchive>
    无效连载(TArchive&安培;归档,const的无符号整型版)
    {
        //这两个看似相同的。如果没有他们中的一个,未注册的投无效
        归档和放大器;提高::系列化:: base_object< AbstractPoint>(*此);
        //归档和放大器; BOOST_SERIALIZATION_BASE_OBJECT_NVP(AbstractPoint);        //这两个看似等价
        归档和放大器; MDATA;
        //归档和放大器; BOOST_SERIALIZATION_NVP(MDATA);
    }    双MDATA;
};诠释的main()
{
    的std :: shared_ptr的< AbstractPoint>点(新点(7.4));    的std ::的ofstream的OutputStream(的test.txt);
    提高::档案:: text_oarchive的outputArchive(OutputStream中);
    outputArchive.register_type<点和GT;();
    outputArchive<<点;
    outputStream.close();    的std :: shared_ptr的< AbstractPoint> pointRead;
    性病:: ifstream的InputStream的(的test.txt);
    提高::档案:: text_iarchive inputArchive(InputStream的);
    inputArchive.register_type<点和GT;();
    inputArchive>> pointRead;    的std :: shared_ptr的<点和GT; castedPoint =的std :: dynamic_pointer_cast<点和GT;(pointRead);
    性病::法院LT&;< castedPoint->&MDATA LT;<的std :: ENDL;
    返回0;
}

另一个主要问题就是在一个真实的环境中注册类(当有链接等),但似乎值得一个单独的问题。

这将是巨大的,有这样的东西在文档中的金标准的例子,但在至少在计算器上:)


解决方案

  • 的boost ::系列化:: base_object VS BOOST_SERIALIZATION_BASE_OBJECT_NVP

该NVP包装只会被要求有元素命名,如XML存档。

除非你使用它, base_object<方式> 是更清洁和更简单


  • 归档和放大器; MDATA; VS 归档和放大器; BOOST_SERIALIZATION_NVP(MDATA);

同上


  • BOOST_SERIALIZATION_ASSUME_ABSTRACT的用处(AbstractPoint);

我以为这只会是一个最优化 - SUP pressing注册类型与每个存档类型的信息,因为你告诉框架它永远不会被反序列化该类型的实例


  • 要求连载()在分级中的类,这并不需要序列化任何东西。

您不需要它,除非你需要大约多态基有类型的信息。当你需要的?当你需要为基本类型的反序列化的指针。

因此​​,如果你有

 结构A {虚拟〜A(); };
结构A:A {};结构C:B {};
结构D:B {};`

的需要 A 序列化(但不是 B )如果你( DE)连载 A * 。您需要序列化 B 如果你(反)序列化 B *

同样,如果你的类型是不是多态(虚拟),或者你不使用它是这样,你不需要任何基础的序列化(例如,如果你(反)序列化 C D 直接)。

最后,如果你有结构A {};结构A:A {}; 没有必要向大家介绍的基本类型升压序列化可言,(你可能只是做系列化从 B在)。

更新响应您的样品:


  1. case1.cpp 看起来不错

  2. case2.cpp 需要即可调用基序列当然;不一定使用base_object因为你需要多态序列化:

     模板<类TArchive>无效连载(TArchive&安培;存档,无符号){
        归档和放大器;提高::系列化:: base_object< AbstractPoint>(*此)
                &安培; MDATA;
        // 要么:
        归档和放大器;的static_cast< AbstractPoint&安培;>(*此)
                &安培; MDATA;
        //甚至只是:
        归档和放大器; mParentData
                &安培; MDATA;
    }


  3. case3.cpp :的确,它酷似情形1,但与动态分配和目标跟踪


  4. case4.cpp :酷似情形1,但与动态分配和对象跟踪; NB!它需要为基本明确序列化!

     模板<类TArchive>无效连载(TArchive&安培;存档,无符号){
        归档和放大器;提高::系列化:: base_object< AbstractPoint>(*此)
                &安培; MDATA;
    }


  5. case5.cpp :是的,但它是更典型的使用 CLASS_EXPORT * 升压/系列化/ export.hpp



  

Bitrot保险:


  
  

I have seen many questions, tutorials, and documentation involving serializing derived classes, and I haven't been able to reach a consensus on several issues, including (and illustrated in the following code):

  • boost::serialization::base_object vs BOOST_SERIALIZATION_BASE_OBJECT_NVP
  • archive & mData; vs archive & BOOST_SERIALIZATION_NVP(mData);
  • The usefulness of BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);
  • Requiring serialize() for a class in the hierarchy that doesn't need to serialize anything.

Code:

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/base_object.hpp>

#include <fstream>

class AbstractPoint
{
public:
    virtual ~AbstractPoint(){}
    virtual void DoSomething() = 0;

    // Even though the class is abstract, we still need this
    template<class TArchive>
    void serialize(TArchive& archive, const unsigned int version)
    {
        // do nothing
    }
};

// This doesn't seem to do anything
//BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);

class Point : public AbstractPoint
{
public:
    Point() = default;
    Point(const double data) : mData(data) {}

    void DoSomething(){}

    template<class TArchive>
    void serialize(TArchive& archive, const unsigned int version)
    {
        // These two seem equivalent. Without one of them, unregistered void cast
        archive & boost::serialization::base_object<AbstractPoint>(*this);
        //archive & BOOST_SERIALIZATION_BASE_OBJECT_NVP(AbstractPoint);

        // These two seem equivalent
        archive & mData;
        //archive & BOOST_SERIALIZATION_NVP(mData);
    }

    double mData;
};

int main()
{
    std::shared_ptr<AbstractPoint> point(new Point(7.4));

    std::ofstream outputStream("test.txt");
    boost::archive::text_oarchive outputArchive(outputStream);
    outputArchive.register_type<Point>();
    outputArchive << point;
    outputStream.close();

    std::shared_ptr<AbstractPoint> pointRead;
    std::ifstream inputStream("test.txt");
    boost::archive::text_iarchive inputArchive(inputStream);
    inputArchive.register_type<Point>();
    inputArchive >> pointRead;

    std::shared_ptr<Point> castedPoint = std::dynamic_pointer_cast<Point>(pointRead);
    std::cout << castedPoint->mData << std::endl;
    return 0;
}

The other major issue is where to register classes in a "real" environment (when there is linking, etc.), but that seems worth a separate question.

It would be great to have a "gold standard" example of these kinds of things in the documentation, but at the least on StackOverflow :)

解决方案

  • boost::serialization::base_object vs BOOST_SERIALIZATION_BASE_OBJECT_NVP

The NVP wrapper is only ever required for archives that have element naming, like XML.

Unless you use it, base_object<> is cleaner and simpler.

  • archive & mData; vs archive & BOOST_SERIALIZATION_NVP(mData);

Ditto

  • The usefulness of BOOST_SERIALIZATION_ASSUME_ABSTRACT(AbstractPoint);

I assume it will merely be an optimization - suppressing registered type information with each archive type, since you told the framework it will never be de-serializing instances of the type

  • Requiring serialize() for a class in the hierarchy that doesn't need to serialize anything.

You don't need it, unless you need the type information about a polymorphic base there. When do you need that? When you need to de-serialize pointers of the base type.

Hence, if you have

struct A{ virtual ~A(); };
struct B:A{};

struct C:B{};
struct D:B{};` 

you will need serialization for A (but not B) if you (de)serialize A*. You will need serialization for B if you (de)serialize B*.

Similarly, if your type is not polymorphic (virtual) or you don't use it as such, you don't need any base serialization (e.g. if you (de)serialize C or D directly).

Finally, if you have struct A{}; struct B:A{}; there is no need to tell Boost Serialization about the base type at all, (you could just do the serialization from within B).

Update in response to your samples:

  1. case1.cpp looks ok
  2. case2.cpp needs to call base serialization, of course; not necessarily using base_object because you require polymorphic serialization:

    template<class TArchive> void serialize(TArchive& archive, unsigned) {
        archive & boost::serialization::base_object<AbstractPoint>(*this)
                & mData;
        // OR:
        archive & static_cast<AbstractPoint&>(*this) 
                & mData;
        // OR even just:
        archive & mParentData 
                & mData;
    }
    

  3. case3.cpp: indeed, it's exactly like case1, but with dynamic allocation and object tracking

  4. case4.cpp: is exactly like case1, but with dynamic allocation and object tracking; NB!! it requires explicitly serializing for the base!

    template<class TArchive> void serialize(TArchive& archive, unsigned) {
        archive & boost::serialization::base_object<AbstractPoint>(*this)
                & mData;
    }
    

  5. case5.cpp: yes, but it's more typical to use the CLASS_EXPORT* macros from boost/serialization/export.hpp

Bitrot insurance:

这篇关于与序列多态类型常见的混乱的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆