2

我减少了一个人为的(最小的)示例。

第 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. 更好的版本 1,创建一个额外的类来定义覆盖,这样它就不会污染派生类。仍然非常具有侵入性。

  3. 做我上面做的事情,并为基类添加运行时错误的 Cereal 函数(在实际代码中,我会抛出 std::logic_error 或其他东西)。

我想最好的是:

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

[ 呵呵,有时候写出来会让你相信答案,嗯?]

  1. 一种强制 Cereal 将类视为抽象基类的显式方法,即使它不是。查看 polymorphic.hpp,很少有使用 std::is_abstract 的地方。可以(?)添加一个谷物::is_abstract 类型特征,客户可以使用它来注入一个类永远不会直接反序列化的知识。

无论如何,这是 TLDR 的方式,感谢您阅读到这里。请告诉我我是不是疯了,或者我是否至少以一种明智的方式定义了“问题空间”。

4

0 回答 0