为什么 boost::serialize 看起来一切正常却无法正常工作? ("unregistered class")

Why does boost::serialize not work despite everything seeming right? ("unregistered class")

我想知道这个。我有一个 C++ 程序,其中包含许多从公共根派生的数据结构,我需要使用 Boost 对它们进行序列化。每个都有一个内联成员函数来接受访问者(因此我可以在没有 "switch" 语句的情况下访问该结构)。

objects 看起来像这样:

.h文件中:

// Graphic component.
struct GraphicComponent : public Component {
  ... data members ...
  void accept(ComponentVisitor &vis) { vis.visitGraphicComponent(*this); }

 private:
  // Serialization routine.
  friend class boost::serialization::access;

template<class Archive>
  void serialize(Archive &a, const unsigned int v);
};
BOOST_CLASS_EXPORT_KEY(GraphicComponent)

// Position component.
struct PositionComponent : public Component {
  ... data members ...
  void accept(ComponentVisitor &vis) { vis.visitPositionComponent(*this); }

 private:
  // Serialization routine.
  friend class boost::serialization::access;

template<class Archive>
  void serialize(Archive &a, const unsigned int v);
};
BOOST_CLASS_EXPORT_KEY(PositionComponent)

...

在 .cpp 文件中,我声明 "serialize" 例程:

BOOST_CLASS_EXPORT_IMPLEMENT(GraphicComponent)
BOOST_CLASS_EXPORT_IMPLEMENT(PositionComponent)
...

template<class Archive>
  void GraphicComponent::serialize(Archive &a, const unsigned int v)
  {
    a & boost::serialization::base_object<Component>(*this);
    ... serialize data members ...
  }

template<class Archive>
  void PositionComponent::serialize(Archive &a, const unsigned int v)
  {
    a & boost::serialization::base_object<Component>(*this);
    ... serialize data members ...
  }

...

我还通过通用 header 包含了 Boost 存档。据我所知,一切看起来都不错。基本组件上还有一个 "BOOST_SERIALIZATION_ASSUME_ABSTRACT",因为 "accept" 是纯虚拟的。

当我 运行 程序并到达它序列化这些东西的地步时,我得到

 what():  unregistered class - derived class not registered or exported

序列化通过指向基本组件的指针发生。

我听说过有关 Boost 序列化和 "libraries" 的麻烦。我使用的构建系统 CMake 被设置为通过将其子组件组装到库中来编译程序,然后将它们放在一个单一的可执行文件中以制作最终程序。这可能是问题所在吗?

此外,Component 派生自 std::enable_shared_from_this(这是 C++11 STL,而不是 Boost)——这可能是问题所在吗?如果是这样,可以做些什么?

如果有帮助,这里有一个有效的 SSCCE(或评论者所说的 MCVE):

Live On Coliru

#include <boost/serialization/access.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

struct ComponentVisitor;

struct Component {
    virtual ~Component() = default;
    virtual void accept(ComponentVisitor &v) = 0;
  private:
    // Serialization routine.
    friend class boost::serialization::access;

    template <class Archive>
    void serialize(Archive &, const unsigned int) {}
};

BOOST_SERIALIZATION_ASSUME_ABSTRACT(Component)

struct GraphicComponent;
struct PositionComponent;

struct ComponentVisitor {
    virtual void visitGraphicComponent(GraphicComponent   const &){};
    virtual void visitPositionComponent(PositionComponent const &){};
};

// Graphic component.
struct GraphicComponent : public Component {
    void accept(ComponentVisitor &vis) { vis.visitGraphicComponent(*this); }

  private:
    // Serialization routine.
    friend class boost::serialization::access;

    template <class Archive>
    void serialize(Archive &a, const unsigned int v);
};
BOOST_CLASS_EXPORT_KEY(GraphicComponent)

// Position component.
struct PositionComponent : public Component {
    void accept(ComponentVisitor &vis) { vis.visitPositionComponent(*this); }

  private:
    // Serialization routine.
    friend class boost::serialization::access;

    template <class Archive>
    void serialize(Archive &a, const unsigned int v);
};

BOOST_CLASS_EXPORT_KEY(PositionComponent)

/////////////////////////////////////////////////////

BOOST_CLASS_EXPORT_IMPLEMENT(GraphicComponent)
BOOST_CLASS_EXPORT_IMPLEMENT(PositionComponent)
//...

template <class Archive>
void GraphicComponent::serialize(Archive &a, const unsigned int)
{
    a &boost::serialization::base_object<Component>(*this);
    //... serialize data members ...
}

template <class Archive>
void PositionComponent::serialize(Archive &a, const unsigned int)
{
    a &boost::serialization::base_object<Component>(*this);
    //... serialize data members ...
}

#include <boost/make_shared.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <sstream>

int main() {
    std::stringstream ss;

    {
        std::vector<boost::shared_ptr<Component> > components {
            boost::make_shared<GraphicComponent>(),
                boost::make_shared<PositionComponent>(),
                boost::make_shared<PositionComponent>(),
                boost::make_shared<GraphicComponent>(),
        };

        boost::archive::text_oarchive oa(ss);
        oa << components;
    }

    {
        std::vector<boost::shared_ptr<Component> > deserialized;

        boost::archive::text_iarchive ia(ss);
        ia >> deserialized;

        struct printer : ComponentVisitor {
            void visitPositionComponent(PositionComponent const & /*pc*/){ std::cout << __PRETTY_FUNCTION__ << "\n"; }
            void visitGraphicComponent(GraphicComponent   const & /*gc*/){ std::cout << __PRETTY_FUNCTION__ << "\n"; }
        } print;

        for (auto c : deserialized)
            c->accept(print);
    }
}

版画

virtual void main()::printer::visitGraphicComponent(const GraphicComponent&)
virtual void main()::printer::visitPositionComponent(const PositionComponent&)
virtual void main()::printer::visitPositionComponent(const PositionComponent&)
virtual void main()::printer::visitGraphicComponent(const GraphicComponent&)

符合预期

备注

  1. 其实,因为你只在特定的TU中使用序列化,你可以考虑使用非侵入式序列化:

    Live On Coliru

    struct ComponentVisitor;
    
    struct Component {
        virtual ~Component() = default;
        virtual void accept(ComponentVisitor &v) = 0;
    };
    
    struct GraphicComponent;
    struct PositionComponent;
    
    struct ComponentVisitor {
        virtual void visitGraphicComponent(GraphicComponent   const &){};
        virtual void visitPositionComponent(PositionComponent const &){};
    };
    
    struct GraphicComponent : public Component {
        void accept(ComponentVisitor &vis) { vis.visitGraphicComponent(*this); }
    };
    
    struct PositionComponent : public Component {
        void accept(ComponentVisitor &vis) { vis.visitPositionComponent(*this); }
    };
    
    /////////////////////////////////////////////////////
    // in the CPP
    #include <boost/serialization/access.hpp>
    #include <boost/serialization/export.hpp>
    #include <boost/serialization/base_object.hpp>
    #include <boost/archive/text_iarchive.hpp>
    #include <boost/archive/text_oarchive.hpp>
    
    BOOST_SERIALIZATION_ASSUME_ABSTRACT(Component)
    BOOST_CLASS_EXPORT(GraphicComponent)
    BOOST_CLASS_EXPORT(PositionComponent)
    
    namespace boost { namespace serialization {
        template <class Archive> void serialize(Archive &, Component&, const unsigned int) {}
        template <class Archive> void serialize(Archive &a, GraphicComponent& obj, const unsigned int) {
            a &boost::serialization::base_object<Component>(obj);
        }
        template <class Archive> void serialize(Archive &a, PositionComponent& obj, const unsigned int) {
            a &boost::serialization::base_object<Component>(obj);
        }
    } }
    

    哪个更干净

  2. 如果您仍想从内部访问私有成员 serialize cf.例如

这是一个部分答案,因为它没有准确解释失败的原因。我已经设法通过将程序编译为单个程序而不是一堆静态链接在一起的库来解决问题,这就是我认为自从文档以来我使用的构建系统必须这样做的方式该系统的在线可用非常简洁,当我将 makefile 放在一起时,我不确定该怎么做。我怀疑这与 Boost 在处理库中的此类代码时遇到麻烦有关。

您需要在图书馆代码中包含存档 class headers。 https://www.boost.org/doc/libs/1_75_0/libs/serialization/doc/special.html#export

Placing BOOST_CLASS_EXPORT in library code will have no effect unless archive class headers are also included. So when building a library, one should include all headers for all the archive classes which he anticipates using.