Зерновые, кажется, требовательны к базовым классам, которые никогда не сериализуются - PullRequest
0 голосов
/ 25 августа 2018

Придуманный (минимальный) пример, который я уменьшил.

Шаг # 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, cerealize:

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);
}

Поработав некоторое время, похоже, нет другого способа "убедить" Cereal в том, что я никогда не хочу напрямую сериализовать (cerealize?) BodyPart,Я нашел два решения, которые работают

  1. Добавьте фиктивную чистую виртуальную функцию в базу, а затем определите ее во всех производных классах.Очевидно, что это глупо, а не то, что я собираюсь делать в реальном коде.Также не совместим с неинтрузивным методом.

  2. Более хорошая версия 1, создайте дополнительный класс для определения переопределения, чтобы он не загрязнил производные классы.Все еще очень навязчиво.

  3. Сделайте то, что я сделал выше, и добавьте функции Cereal для базового класса с ошибкой времени выполнения (в реальном коде я бы бросил std :: logic_error иличто-то).

Полагаю, было бы неплохо:

Cereal волшебным образом поступает правильно и откладывает поиск функций сериализации базового класса, пока я на самом деле не сериализую его. Это невозможно, потому что модуль перевода для десериализации ничего не знает о том, что было сериализовано (может быть где угодно).Это знание нужно как-то внедрить в выходной архив.

[Хех, иногда выписывая его, убеждает вас в ответе, а?]

Явный способ заставить Cereal рассматривать класс как абстрактный базовый класс, даже если это не так.Глядя на polymorphic.hpp, есть несколько мест, где используется std :: is_abstract.Можно (?) Добавить черту типа cereal :: is_abstract, которую клиент может использовать, чтобы внедрить информацию о том, что класс никогда не де-сериализуется напрямую.

В любом случае, это способ TLDR,Спасибо за чтение этого далеко.Пожалуйста, скажите мне, если я сумасшедший или я хотя бы определенно определил «проблемное пространство».

...