【问题标题】:Cereal Polymorphic Serialization couldnt find serialization function谷物多态序列化找不到序列化函数
【发布时间】:2019-01-05 20:50:34
【问题描述】:

我目前正在尝试使用静态函数创建一个可序列化的基类,以序列化和反序列化派生类对象。 我已阅读 Cereal Documentation for registering Polymorphic Relations 以及如何注册类型以及如何声明序列化函数。 目标是像这样使用 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.

这是我的代码:

可序列化的.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);
};
可序列化的.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;
}
测试对象.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)
测试对象.cpp
#include "TestObject.hpp"
TestObject::TestObject(const std::string& name)
    :TestObject(name){}
主文件
#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 ==========

【问题讨论】:

  • 添加公共默认ctor?
  • @Yakk 添加了一个空的公共默认 ctor,但它仍然无法正常工作。
  • 在哪种类型上?每个人?如果没有多态性(即使用智能指针)作为第一步,你能做到这一点吗?
  • @Yakk-AdamNevraumont 我已编辑代码以将默认构造函数添加到每个类。它在没有多态类型的情况下工作。
  • 其次:在Serializable::serialize 中创建一个本地 对象ss,然后通过reference 返回该对象。你不觉得可能有问题吗? (考虑到函数结束后局部变量会被销毁......);)

标签: c++ visual-studio serialization cereal


【解决方案1】:

由于您尝试序列化 shared_ptr(),因此您应该包含 &lt;cereal/types/memory.hpp&gt;。

【讨论】:

  • 我必须在哪里包含 ?
  • 在Serializable.cpp
  • 它仍然无法正常工作,但如果我同时进行这两项更改,则会减少 1 个输入错误。
  • @MNCODE 那么剩下的错误信息是什么?它与哪一行有关?
  • (TestObject.cpp)cereal.hpp(851): error C2338: cereal could not find any input serialization functions for the provided type and archive combination. (TestObject.cpp) grain.hpp(761): note: see reference to function template instantiation 'ArchiveType &amp;cereal::InputArchive&lt;ArchiveType,1&gt;::processImpl&lt;Base,0&gt;(const T &amp;)' being compiled 1&gt; with 1&gt; [ 1&gt; ArchiveType=cereal::PortableBinaryInputArchive, 1&gt; Base=Serializable, 1&gt; T=Serializable 1&gt; ]
【解决方案2】:

Afaik,如果你想调用cereal::base_class&lt;Serializable&gt;(this),那么基类还需要一个serialize(Archive&amp; ar) 函数。由于没有理由在您的示例中序列化基类(无成员变量),让我们将 TestObject serialize 简化为 ar(name);

那么您所缺少的只是 Serializable.cpp 中的 #include &lt;cereal/types/polymorphic.hpp&gt; 或者更好的 #include &lt;cereal/types/memory.hpp&gt;。如cereal Docs-Polymorphism 中所述,此包含允许PortableBinary[Input/Output]Archive 查看您在派生中设置的CEREAL_REGISTER_POLYMORPHIC_RELATION。

例子:

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

输出:

Test

【讨论】:

  • 感谢您为我工作。但是为什么我需要把它包括在内呢?
  • @MNCODE 我找到了Cereal documentation on polymorphism。它在 cmets 中说“包括多态序列化和注册机制”。你设置的CEREAL_REGISTER_POLYMORPHIC_RELATION 如果不包含它,PortableBinaryOutputArchive 可能看不到它。
  • @MNCODE 还注意到我在您的代码中所做的其他修改:按引用传递、d​​ynamic_pointer_cast、覆盖等。我认为这些改进了代码(auto 很好,但不是必需的) ,但这只是一种意见。也许你会觉得这些很有趣。编辑:哦,不要忘记你的基本函数的虚拟析构函数。 Make a habit of using this, to prevent memory leaks.
  • @MNCODE 嗯,我重读了the docs 并参考了底部:“确保存在从派生类型到基本类型的路径”。那里都有解释。
  • 感谢您的提示对我有很大帮助:)。但我意识到如果我将方法virtual bool isAccessible() = 0; 更新为virtual std::string getType() { return "Serializable";}; 之类的东西,Cereal 将无法再次找到任何输出序列化函数。如果我将其更改为 virtual std::string getType() = 0; 它可以工作。我真的不需要这个功能,但我想知道为什么它不起作用。
猜你喜欢
  • 2015-07-06
  • 2018-03-12
  • 1970-01-01
  • 2016-08-21
  • 2021-01-05
  • 2017-09-03
  • 2016-12-31
  • 2018-08-08
  • 1970-01-01
相关资源
最近更新 更多