谷物似乎对从未序列化的基类很挑剔

时间:2018-08-25 15:09:19

标签: c++ serialization polymorphism cereal

我简化的人为(最小)示例。

第1步要使基本类型带有枚举的“种类”字段,该字段必须由派生的构造函数填充。请注意,由于非默认构造函数会初始化BodyPart,因此没有理由为其进行序列化。

我们确实必须有一个虚拟析构函数,因为我们将创建shared_ptr并将其传递给周围,并且我们要确保不执行切片〜BodyPart。

struct BodyPart
{
    enum class Kind {
        Head,
        Shoulder,
        Knee,
        Toe,
    };
    BodyPart(Kind _kind) : kind(_kind) {}
    Kind const kind;
    virtual ~BodyPart() = default;

    /* ---- Should never happen, but added to make Cereal happy that this can be in/out cerealized --- */
    template<class Archive>
    void serialize( Archive & ar )
    {
        (void)ar; assert(0);
    }

    template <class Archive>
    static void load_and_construct( Archive & ar, cereal::construct<BodyPart> & construct )
    {
        (void)ar; (void)construct; assert(0);
    }
    /* ---- end make cereal happy --- */

};

步骤2,添加带有一些实际数据的派生类

struct Head : BodyPart
{
    Head(int _eyeCount) : BodyPart(Kind::Head), eyeCount(_eyeCount) {}
    int const eyeCount;

    template<class Archive>
    void serialize( Archive & ar )
    {
        ar( eyeCount );
    }

    template <class Archive>
    static void load_and_construct( Archive & ar, cereal::construct<Head> & construct )
    {
      int x;
      ar( x );
      construct( x );
    }
};

第3步,执行所需的注册

CEREAL_REGISTER_TYPE(Head)
CEREAL_REGISTER_POLYMORPHIC_RELATION(BodyPart, Head)

第4步,谷物化:

std::shared_ptr<BodyPart> head = std::make_shared<Head>(3);
std::string data;

{
    std::ostringstream oss(std::ios::binary);
    cereal::PortableBinaryOutputArchive oarchive(oss);
    oarchive(head);
    data = oss.str();
}

decltype(head) head2;
{
    std::istringstream iss(data);
    cereal::PortableBinaryInputArchive iarchive(iss);
    iarchive(head2);
}

经过一段时间的讨论,似乎没有其他方法可以“说服”谷物,而我从来不想直接序列化(谷物化?)BodyPart。我找到了两个可行的解决方案

  1. 向基中添加一个虚拟纯虚函数,然后在所有派生类中对其进行定义。显然,这是愚蠢的,不是我要在实际代码中要做的事情。也与非侵入式方法不兼容。

  2. Nicer版本1,创建一个额外的类来定义覆盖,以便不污染派生类。仍然很侵入。

  3. 执行上述操作,并为基类添加Cereal函数,并出现运行时错误(在实际代码中,我会抛出std :: logic_error或类似内容)。

我猜会很好的是:

  1. Cereal神奇地做了正确的事情,并推迟寻找基类的序列化功能,直到我真正对其进行序列化。这是不可能的,因为反序列化的翻译单元对序列化的内容一无所知(可能是任何地方)。必须以某种方式将这些知识注入到输出档案中。

[嘿,有时写出来使您相信答​​案,是吗? ]

  1. 一种强制Cereal将类视为抽象基类的显式方法,即使事实并非如此。查看polymorphic.hpp,几乎没有使用std :: is_abstract的地方。可能(?)可以添加客户可以使用的谷物:: is_abstract类型特征来注入知识,即永远不会直接对序列进行反序列化。

无论如何,这是TLDR的方式,请阅读本文。请告诉我我是不是疯了,还是至少以明智的方式定义了“问题空间”。

0 个答案:

没有答案