Полиморфная сериализация зерновых не смогла найти функцию сериализации

В настоящее время я пытаюсь создать Serializable базового класса со статическими функциями для сериализации и десериализации производных объектов класса. Я прочитал документацию Cereal о регистрации полиморфных отношений и о том, как регистрировать типы, а также о том, как объявлять функции сериализации. Цель состоит в том, чтобы использовать Serializable следующим образом:

std::stringstream& ss Serializable::serialize(test);

Я использую инструменты платформы Visual Studio 2017 (v141). Целевой Windows SDK — 10.0.17134.0.

Но я не могу создать свое приложение и получить эти 2 ошибки 3 раза:

Error   C2338   cereal could not find any output serialization functions for the provided type and archive combination.

Error   C2338   cereal could not find any input serialization functions for the provided type and archive combination.

Это мой код:

Serializable.hpp
#pragma once
#include <string>
#include <cereal/archives/portable_binary.hpp>

class Serializable 
{
public:
Serializable() = default;
~Serializable() = default;
    virtual bool isAccessible() = 0;

    static std::stringstream serialize(std::shared_ptr<Serializable> serializable);
    static std::shared_ptr<Serializable> deserialize(std::stringstream& serialized);
};
Serializable.cpp
#include "Serializable.hpp"

std::stringstream Serializable::serialize(std::shared_ptr<Serializable> serializable)
{
    std::stringstream ss;
    {
        cereal::PortableBinaryOutputArchive  ar(ss);
        ar << serializable;
    }
    return ss;
}

std::shared_ptr<Serializable> Serializable::deserialize(std::stringstream& serialized)
{
    cereal::PortableBinaryInputArchive ar(serialized);
    std::shared_ptr<Serializable> result = nullptr;
    ar >> result;
    return result;
}
TestObject.hpp
#pragma once
#include "Serializable.hpp"
#include <string>
#include <cereal/types/string.hpp>
#include <cereal/types/polymorphic.hpp>
#include <cereal/types/base_class.hpp>

class TestObject : public Serializable
{
public:
TestObject() = default;
TestObject(const std::string& name);
~TestObject() = default;
    std::string getName() const { return this->name; };



    template<class Archive>
    void serialize(Archive& ar)
    {
        ar(cereal::base_class<Serializable>(this), name);
    };

    virtual bool isAccessible() {
        return true;
    };

private:
    std::string name;
};

CEREAL_REGISTER_TYPE(TestObject)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Serializable,TestObject)
TestObject.cpp
#include "TestObject.hpp"
TestObject::TestObject(const std::string& name)
    :TestObject(name){}
main.cpp
#include "TestObject.hpp"
#include "Serializable.hpp"
#include <memory>
#include <iostream>
#include <string>

int main(int argc, char **argv)
{
    std::shared_ptr<Serializable> test(new TestObject("Test"));
    auto ss = Serializable::serialize(test);
    std::shared_ptr<Serializable> deserialized = Serializable::deserialize(ss);
    auto test2 = dynamic_cast<TestObject*>(deserialized.get());
    std::cout << test2->getName();
    system("timeout 3");
    return 0;
}

Это полная ошибка здания:

1>------ Build started: Project: SerializableTest, Configuration: Debug Win32 ------
1>TestObject.cpp
1>Serializable.cpp
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\lib\cereal\include\cereal\cereal.hpp(462): error C2338: cereal could not find any output serialization functions for the provided type and archive combination.
1>
1> Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these).
1> Serialize functions generally have the following signature:
1>
1> template<class Archive>
1>   void serialize(Archive & ar)
1>   {
1>     ar( member1, member2, member3 );
1>   }
1>
1>
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\lib\cereal\include\cereal\cereal.hpp(347): note: see reference to function template instantiation 'ArchiveType &cereal::OutputArchive<ArchiveType,1>::processImpl<std::shared_ptr<Serializable>,0>(const T &)' being compiled
1>        with
1>        [
1>            ArchiveType=cereal::PortableBinaryOutputArchive,
1>            T=std::shared_ptr<Serializable>
1>        ]
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\lib\cereal\include\cereal\cereal.hpp(347): note: see reference to function template instantiation 'ArchiveType &cereal::OutputArchive<ArchiveType,1>::processImpl<std::shared_ptr<Serializable>,0>(const T &)' being compiled
1>        with
1>        [
1>            ArchiveType=cereal::PortableBinaryOutputArchive,
1>            T=std::shared_ptr<Serializable>
1>        ]
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\lib\cereal\include\cereal\cereal.hpp(290): note: see reference to function template instantiation 'void cereal::OutputArchive<cereal::PortableBinaryOutputArchive,1>::process<std::shared_ptr<Serializable>&>(T)' being compiled
1>        with
1>        [
1>            T=std::shared_ptr<Serializable> &
1>        ]
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\lib\cereal\include\cereal\cereal.hpp(290): note: see reference to function template instantiation 'void cereal::OutputArchive<cereal::PortableBinaryOutputArchive,1>::process<std::shared_ptr<Serializable>&>(T)' being compiled
1>        with
1>        [
1>            T=std::shared_ptr<Serializable> &
1>        ]
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\serializable.cpp(8): note: see reference to function template instantiation 'ArchiveType &cereal::OutputArchive<ArchiveType,1>::operator <<<std::shared_ptr<Serializable>&>(T)' being compiled
1>        with
1>        [
1>            ArchiveType=cereal::PortableBinaryOutputArchive,
1>            T=std::shared_ptr<Serializable> &
1>        ]
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\serializable.cpp(8): note: see reference to function template instantiation 'ArchiveType &cereal::OutputArchive<ArchiveType,1>::operator <<<std::shared_ptr<Serializable>&>(T)' being compiled
1>        with
1>        [
1>            ArchiveType=cereal::PortableBinaryOutputArchive,
1>            T=std::shared_ptr<Serializable> &
1>        ]
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\lib\cereal\include\cereal\cereal.hpp(851): error C2338: cereal could not find any input serialization functions for the provided type and archive combination.
1>
1> Types must either have a serialize function, load/save pair, or load_minimal/save_minimal pair (you may not mix these).
1> Serialize functions generally have the following signature:
1>
1> template<class Archive>
1>   void serialize(Archive & ar)
1>   {
1>     ar( member1, member2, member3 );
1>   }
1>
1>
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\lib\cereal\include\cereal\cereal.hpp(730): note: see reference to function template instantiation 'ArchiveType &cereal::InputArchive<ArchiveType,1>::processImpl<std::shared_ptr<Serializable>,0>(const T &)' being compiled
1>        with
1>        [
1>            ArchiveType=cereal::PortableBinaryInputArchive,
1>            T=std::shared_ptr<Serializable>
1>        ]
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\lib\cereal\include\cereal\cereal.hpp(730): note: see reference to function template instantiation 'ArchiveType &cereal::InputArchive<ArchiveType,1>::processImpl<std::shared_ptr<Serializable>,0>(const T &)' being compiled
1>        with
1>        [
1>            ArchiveType=cereal::PortableBinaryInputArchive,
1>            T=std::shared_ptr<Serializable>
1>        ]
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\lib\cereal\include\cereal\cereal.hpp(660): note: see reference to function template instantiation 'void cereal::InputArchive<cereal::PortableBinaryInputArchive,1>::process<std::shared_ptr<Serializable>&>(T)' being compiled
1>        with
1>        [
1>            T=std::shared_ptr<Serializable> &
1>        ]
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\lib\cereal\include\cereal\cereal.hpp(660): note: see reference to function template instantiation 'void cereal::InputArchive<cereal::PortableBinaryInputArchive,1>::process<std::shared_ptr<Serializable>&>(T)' being compiled
1>        with
1>        [
1>            T=std::shared_ptr<Serializable> &
1>        ]
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\serializable.cpp(17): note: see reference to function template instantiation 'ArchiveType &cereal::InputArchive<ArchiveType,1>::operator >><std::shared_ptr<Serializable>&>(T)' being compiled
1>        with
1>        [
1>            ArchiveType=cereal::PortableBinaryInputArchive,
1>            T=std::shared_ptr<Serializable> &
1>        ]
1>k:\programme\c++\serializabletest\networkdebugs\serializabletest\serializable.cpp(17): note: see reference to function template instantiation 'ArchiveType &cereal::InputArchive<ArchiveType,1>::operator >><std::shared_ptr<Serializable>&>(T)' being compiled
1>        with
1>        [
1>            ArchiveType=cereal::PortableBinaryInputArchive,
1>            T=std::shared_ptr<Serializable> &
1>        ]
1>main.cpp
1>Generating Code...
1>Done building project "SerializableTest.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

person MNCODE    schedule 29.07.2018    source источник
comment
Добавить общедоступный ctor по умолчанию?   -  person Yakk - Adam Nevraumont    schedule 30.07.2018
comment
@Yakk добавил пустой общедоступный ctor по умолчанию, и он все еще не работает.   -  person MNCODE    schedule 30.07.2018
comment
На каком типе? Каждый? Можете ли вы заставить это работать без полиморфизма (то есть с умными указателями) в качестве первого шага?   -  person Yakk - Adam Nevraumont    schedule 30.07.2018
comment
@Yakk-AdamNevraumont Я отредактировал код, чтобы добавить конструкторы по умолчанию в каждый класс. Он работает без полиморфных типов.   -  person MNCODE    schedule 30.07.2018
comment
Извините, это все, что у меня есть. Может помочь, если вы опубликуете полный журнал ошибок компилятора от компилятора (компилятор иногда скрывает подробности о вас)   -  person Yakk - Adam Nevraumont    schedule 30.07.2018
comment
@Yakk-AdamNevraumont Я предоставил полную ошибку сборки. Есть ли какие-либо флаги для компилятора, чтобы получить более полный журнал ошибок?   -  person MNCODE    schedule 31.07.2018
comment
заканчивайте определения классов с помощью ;. то есть class Foo {};   -  person JHBonarius    schedule 31.07.2018
comment
@JHBonarius просто забыл об этом в этом примере, но спасибо, я отредактирую свой вопрос :)   -  person MNCODE    schedule 31.07.2018
comment
во-вторых: в Serializable::serialize вы создаете локальный объект ss, который вы возвращаете по ссылке. Вам не кажется, что это может создать проблему? (учитывая, что локальные переменные уничтожаются после завершения функции...);)   -  person JHBonarius    schedule 31.07.2018
comment
@JHBonarius, извините, я новичок в C++. std::stringstream не копируется, поэтому я использовал ссылку. Но я не думал о последствиях.   -  person MNCODE    schedule 31.07.2018
comment
Их нельзя копировать, но они могут перемещаться (начиная с C++11 и далее)... см. мой код ниже   -  person JHBonarius    schedule 31.07.2018


Ответы (2)


Afaik, Если вы хотите вызывать cereal::base_class<Serializable>(this), то в базовом классе тоже нужна функция serialize(Archive& ar). Поскольку в вашем примере нет причин сериализовать базовый класс (нет переменных-членов), давайте упростим TestObject serialize до ar(name);

Тогда все, что вам не хватает, это #include <cereal/types/polymorphic.hpp> или, может быть, лучше #include <cereal/types/memory.hpp> в Serializable.cpp. Как описано в полиморфизм документов cereal, это включение позволяет PortableBinary[Input/Output]Archive видеть CEREAL_REGISTER_POLYMORPHIC_RELATION то, что вы устанавливаете в своих производных.

Пример:

Сериализуемый.hpp

#pragma once
#include <sstream>
#include <memory>

class Serializable
{
public:
    Serializable() = default;
    virtual ~Serializable() = default;
    virtual bool isAccessible() = 0;

    static std::stringstream serialize(const std::shared_ptr<Serializable>& serializable);
    static std::shared_ptr<Serializable> deserialize(std::stringstream& serialized);
};

Сериализуемый.cpp

#include "Serializable.hpp"
#include <cereal/archives/portable_binary.hpp>
#include <cereal/types/memory.hpp>

std::stringstream Serializable::serialize(const std::shared_ptr<Serializable>& serializable)
{
    std::stringstream ss;
    cereal::PortableBinaryOutputArchive ar(ss);
    ar(serializable);
    return ss;
}

std::shared_ptr<Serializable> Serializable::deserialize(std::stringstream& serialized)
{
    cereal::PortableBinaryInputArchive ar(serialized);
    std::shared_ptr<Serializable> result;
    ar(result);
    return result;
}

TestObject.hpp

#pragma once
#include "Serializable.hpp"
#include <string>
#include <cereal/types/polymorphic.hpp>

class TestObject : public Serializable
{
public:
    TestObject() = default;
    TestObject(const std::string& name) : name(name) {}
    ~TestObject() = default;
    std::string getName() const { return this->name; };

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

    bool isAccessible() override { return true; };
private:
    std::string name;
};

CEREAL_REGISTER_TYPE(TestObject);
CEREAL_REGISTER_POLYMORPHIC_RELATION(Serializable, TestObject)

main.cpp

#include "TestObject.hpp"
#include "Serializable.hpp"
#include <memory>
#include <sstream>
#include <iostream>

int main(int argc, char **argv)
{
    auto test = std::dynamic_pointer_cast<Serializable>(std::make_shared<TestObject>("Test"));
    auto ss = Serializable::serialize(test);
    auto deserialized = Serializable::deserialize(ss);
    auto test2 = std::dynamic_pointer_cast<TestObject>(deserialized);
    std::cout << test2->getName();

    return 0;
}

Выход:

Test
person JHBonarius    schedule 31.07.2018
comment
Спасибо, что сработало для меня. Но зачем мне его туда включать? - person MNCODE; 31.07.2018
comment
@MNCODE Я получил документацию Cereal по полиморфизму. В комментариях написано Включить полиморфную сериализацию и механизмы регистрации. Установленный вами CEREAL_REGISTER_POLYMORPHIC_RELATION, вероятно, не будет виден PortableBinaryOutputArchive, если вы его не включите. - person JHBonarius; 31.07.2018
comment
@MNCODE также обратите внимание на другие изменения, которые я внес в ваш код: передача по ссылке, dynamic_pointer_cast, переопределение и т. д. Я думаю, что они улучшают код (auto хорошо, но не обязательно), но это просто мнение. Может быть, вы найдете это интересным. редактировать: о, и не забудьте виртуальный деструктор вашей базовой функции. Используйте это, чтобы предотвратить утечку памяти. - person JHBonarius; 31.07.2018
comment
@MNCODE хм, я перечитал документы и обратился к нижней части: Обеспечение пути существует от производного к базовому типу. Там все объясняется. - person JHBonarius; 31.07.2018
comment
Спасибо за ваши советы, которые мне очень помогли :). Но я понял, что если я обновлю метод virtual bool isAccessible() = 0; до чего-то вроде virtual std::string getType() { return "Serializable";}; Cereal, я больше не смогу найти какую-либо функцию сериализации вывода. Если я изменю его на virtual std::string getType() = 0;, он сработает. Мне действительно не нужна эта функция, но мне интересно, почему она не работает. - person MNCODE; 31.07.2018
comment
@MNCODE, вы можете открыть новый вопрос по этому поводу. Слишком подробно, чтобы отвечать в комментариях. - person JHBonarius; 31.07.2018

Поскольку вы пытаетесь сериализовать shared_ptr(), вы должны включить <cereal/types/memory.hpp>.

person Swordfish    schedule 29.07.2018
comment
Где я должен включить ‹cereal/types/memory.hpp›? - person MNCODE; 29.07.2018
comment
Он все еще не работает, но с ошибкой ввода меньше 1, если я делаю оба изменения. - person MNCODE; 29.07.2018
comment
@MNCODE, так что же это за оставшееся сообщение об ошибке и к какой строке оно относится? - person Swordfish; 29.07.2018
comment
(TestObject.cpp)зерновые.hpp(851): error C2338: cereal could not find any input serialization functions for the provided type and archive combination. (TestObject.cpp) зерновые.hpp(761): note: see reference to function template instantiation 'ArchiveType &cereal::InputArchive<ArchiveType,1>::processImpl<Base,0>(const T &)' being compiled 1> with 1> [ 1> ArchiveType=cereal::PortableBinaryInputArchive, 1> Base=Serializable, 1> T=Serializable 1> ] - person MNCODE; 29.07.2018
comment
Это ошибки сборки. Просто получаю хлопья.hpp(462) для функции сериализации вывода и хлопья.hpp(851) для функции сериализации ввода. - person MNCODE; 29.07.2018
comment
@MNCODE: deserialize(): ar >> *result; - person Swordfish; 29.07.2018
comment
Все еще не работает и получает те же сообщения об ошибках - person MNCODE; 29.07.2018
comment
Cereal не требует разыменования общих указателей. См. документацию. - person JHBonarius; 31.07.2018