Придуманный (минимальный) пример, который я уменьшил.
Шаг # 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, создайте дополнительный класс для определения переопределения, чтобы он не загрязнил производные классы.Все еще очень навязчиво.
Сделайте то, что я сделал выше, и добавьте функции Cereal для базового класса с ошибкой времени выполнения (в реальном коде я бы бросил std :: logic_error иличто-то).
Полагаю, было бы неплохо:
Cereal волшебным образом поступает правильно и откладывает поиск функций сериализации базового класса, пока я на самом деле не сериализую его. Это невозможно, потому что модуль перевода для десериализации ничего не знает о том, что было сериализовано (может быть где угодно).Это знание нужно как-то внедрить в выходной архив.
[Хех, иногда выписывая его, убеждает вас в ответе, а?]
Явный способ заставить Cereal рассматривать класс как абстрактный базовый класс, даже если это не так.Глядя на polymorphic.hpp, есть несколько мест, где используется std :: is_abstract.Можно (?) Добавить черту типа cereal :: is_abstract, которую клиент может использовать, чтобы внедрить информацию о том, что класс никогда не де-сериализуется напрямую.
В любом случае, это способ TLDR,Спасибо за чтение этого далеко.Пожалуйста, скажите мне, если я сумасшедший или я хотя бы определенно определил «проблемное пространство».