Почему boost::serialize не работает, несмотря на то, что все кажется правильным? (незарегистрированный класс)

Я задаюсь этим вопросом. У меня есть программа на C++ с несколькими структурами данных, происходящими из общего корня, и мне нужно сериализовать их с помощью Boost. Каждый из них имеет встроенную функцию-член для приема посетителя (поэтому я могу посетить структуру без оператора «переключатель»).

Объекты выглядят так:

В файле .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 я объявляю процедуры «сериализации»:

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_SERIALIZATION_ASSUME_ABSTRACT», поскольку «accept» является чисто виртуальным.

Когда я запускаю программу и дохожу до того момента, когда она сериализует этот материал, я получаю

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

Сериализация происходит через указатель на базовый Компонент.

Я слышал о проблемах, связанных с сериализацией Boost и «библиотеками». Система сборки, которую я использовал, CMake, настроена на компиляцию программы путем сборки ее подкомпонентов в библиотеки, а затем объединения их в один исполняемый файл для создания окончательной программы. Может ли это быть проблема?

Кроме того, Component происходит от std::enable_shared_from_this (это C++11 STL, а не Boost) - может ли это быть проблемой? Если да, то что можно с этим сделать?


person The_Sympathizer    schedule 22.06.2015    source источник
comment
опубликуйте минимальный, полный и проверяемый пример   -  person m.s.    schedule 22.06.2015
comment
У меня была аналогичная проблема в моей мультибиблиотеке плюс код сериализации Boost. Я не знаю, в чем проблема, но мое решение состоит в том, чтобы создать фиктивную функцию, которая ничего не делает, в файлах исходного кода/cpp, где реализована сериализация, и вызвать эту функцию в функции main. Код фиктивной функции — static volatile int g_iDummyInitialization(0); void InitModule00Serialzation(void) { g_iDummyInitialization = 1; }. В моем случае помогает.   -  person megabyte1024    schedule 23.06.2015


Ответы (3)


Если это поможет, вот рабочий 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 cf. например

person sehe    schedule 22.06.2015
comment
Добавлены два примечания об организации кода. Я сделал это, потому что похоже, что вы пытаетесь скрыть ускоренную сериализацию из заголовка. Вы можете добиться этого намного яснее :) - person sehe; 22.06.2015

Это частичный ответ, так как он точно не объясняет, почему это не удалось. Мне удалось решить проблему, скомпилировав программу как единую программу, а не набор библиотек, которые затем статически связаны друг с другом. доступный в Интернете для системы, был краток, и когда я собрал make-файлы, я не был уверен, как именно это сделать. Я подозреваю, что это как-то связано с проблемой Boost при работе с таким кодом в библиотеках.

person The_Sympathizer    schedule 22.06.2015
comment
это объясняет это. Поскольку ваша программа не ссылается явно на производный класс, код производного класса не вносится компоновщиком. Макросы экспорта позаботятся об этом для DLLS и должны сделать то же самое для статической компоновки. Но не зная, какой компоновщик, компилятор, переключатели компилятора связаны с видимостью символов и т. д., было бы трудно дать больше информации. - person Robert Ramey; 25.11.2015

Вам необходимо включить заголовки архивных классов в коды вашей библиотеки. https://www.boost.org/doc/libs/1_75_0/libs/serialization/doc/special.html#export

Размещение BOOST_CLASS_EXPORT в коде библиотеки не будет иметь никакого эффекта, если также не включены заголовки классов архива. Поэтому при создании библиотеки следует включить все заголовки для всех классов архива, которые он предполагает использовать.

person Skywave    schedule 24.06.2021