为什么 boost::serialize 不起作用,尽管一切看起来都正确?( "unregistered class" )

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

本文关键字:unregistered class 不起作用 serialize boost 为什么 看起来      更新时间:2023-10-16

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

对象看起来像这样:

在.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 ...
  }
...

我还通过一个公共头包含了Boost存档。据我所知,一切看起来都很好。在基本组件上还有一个"BOOST_RIALIZATION_ASSUME_ABSTRACT",因为"accept"是纯虚拟的。

当我运行程序并将其序列化时,我得到了

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

序列化通过指向基本组件的指针进行。

我听说过有关Boost序列化和"库"的问题。我使用的构建系统CMake是通过将程序的子组件组装到库中,然后将这些子组件组合成一个可执行文件来编译程序的。这可能是问题所在吗?

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

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

在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中使用序列化,因此可以考虑使用非侵入式序列化:

    在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内部访问私人会员,请参阅例如

    • 获取非侵入性boost序列化的专用数据成员C++

这是一个部分答案,因为它没有确切解释它失败的原因。我已经设法解决了这个问题,把程序编译成一个单独的程序,而不是一堆静态链接在一起的库,这就是我认为我必须用我正在使用的构建系统来完成的方式,因为该系统的在线文档很简洁,当我把makefile放在一起时,我不知道该怎么做。我怀疑这与Boost在处理库中的此类代码时遇到的问题有关。

您需要在库代码中包含归档类标头。https://www.boost.org/doc/libs/1_75_0/libs/serialization/doc/special.html#export

除非还包含归档类头,否则在库代码中放置BOOST_CLASS_EXPORT将无效。因此,在构建库时,应该包括他预期使用的所有归档类的所有头。