1

I'm currently trying to create a Base Class Serializable with static functions to serialize and deserialize derived Class Objects. I've read the Cereal Documentation for registering Polymorphic Relations and how to register types and also how to declare serialization functions. The Goal is to use Serializable like this:

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

I am using Visual Studio Platform Tools 2017(v141). The Target Windows SDK is 10.0.17134.0

But I cant build my application and get these 2 errors 3x:

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.

This is my Code:

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

This is the complete building Error:

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 ==========
MNCODE
  • 159
  • 3
  • 16

2 Answers2

1

Afaik, If you want to call cereal::base_class<Serializable>(this), then the base class also needs a serialize(Archive& ar) function. As there is no reason to serialize the base class in your example (no member variables), let's simplify the TestObject serialize to ar(name);

Then all you're missing is a #include <cereal/types/polymorphic.hpp> or maybe better #include <cereal/types/memory.hpp> in the Serializable.cpp. As described in the cereal Docs-Polymorphism, this include allows PortableBinary[Input/Output]Archive to see the CEREAL_REGISTER_POLYMORPHIC_RELATION that you set in your derivatives.

Example:

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

Serializable.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;
}

Output:

Test
JHBonarius
  • 10,824
  • 3
  • 22
  • 41
  • Thank you that worked for me. But why do I need to include it there ? – MNCODE Jul 31 '18 at 09:26
  • @MNCODE I sourced the [Cereal documentation on polymorphism](https://uscilab.github.io/cereal/polymorphism.html). It says in the comments "Include the polymorphic serialization and registration mechanisms". The `CEREAL_REGISTER_POLYMORPHIC_RELATION` you set is probably not seen by `PortableBinaryOutputArchive` if you don't include it. – JHBonarius Jul 31 '18 at 09:29
  • @MNCODE also note the other modifications I made in your code: pass-by-reference, dynamic_pointer_cast, override, etc. I think these improve the code (`auto` is nice, but not necessary), but that's just an opinion. Maybe you find these interesting. edit: oh, and don't forget the virtual destructor of your base function. [Make a habit of using this, to prevent memory leaks.](https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors) – JHBonarius Jul 31 '18 at 09:33
  • @MNCODE hmm, I reread [the docs](https://uscilab.github.io/cereal/polymorphism.html) and refer to the bottom part: "Ensuring a path exists from derived to base type". All is explained there. – JHBonarius Jul 31 '18 at 09:57
  • Thanks for your tips that helped me alot :). But I realized if I update the method `virtual bool isAccessible() = 0;` to something like `virtual std::string getType() { return "Serializable";};` Cereal can't find any output serialization function again. If i change it to `virtual std::string getType() = 0;` it works. I don't really need this feature but I wonder why it is not working. – MNCODE Jul 31 '18 at 12:32
  • @MNCODE you can open a new question on that. Too elaborate to answer in the comments. – JHBonarius Jul 31 '18 at 12:50
0

Since you're trying to serialize a shared_ptr() you should include <cereal/types/memory.hpp>.

Swordfish
  • 12,971
  • 3
  • 21
  • 43
  • Where do i have to include ? – MNCODE Jul 29 '18 at 12:45
  • in `Serializable.cpp` – Swordfish Jul 29 '18 at 12:48
  • It's still not working but with 1 input error less if i do both changes. – MNCODE Jul 29 '18 at 12:56
  • @MNCODE so what is the remaining error message and which line does it concern? – Swordfish Jul 29 '18 at 13:00
  • (TestObject.cpp)cereal.hpp(851): `error C2338: cereal could not find any input serialization functions for the provided type and archive combination.` (TestObject.cpp) cereal.hpp(761): `note: see reference to function template instantiation 'ArchiveType &cereal::InputArchive::processImpl(const T &)' being compiled 1> with 1> [ 1> ArchiveType=cereal::PortableBinaryInputArchive, 1> Base=Serializable, 1> T=Serializable 1> ]` – MNCODE Jul 29 '18 at 13:02
  • These are build errors. Just getting cereal.hpp(462) for outpout serialization function and cereal.hpp(851) for input serialization function. – MNCODE Jul 29 '18 at 13:07
  • @MNCODE: `deserialize()`: `ar >> *result;` – Swordfish Jul 29 '18 at 13:09
  • Still not working and getting the same error messages – MNCODE Jul 29 '18 at 13:11
  • Cereal does not require you to dereference shared pointers. See [the docs](https://uscilab.github.io/cereal/polymorphism.html). – JHBonarius Jul 31 '18 at 09:23