Подтвердить что ты не робот

Где поставить BOOST_CLASS_EXPORT для boost:: serialization?

Я пытаюсь сериализовать указатель на полиморфный класс Shape. Поэтому мне нужно использовать макрос BOOST_CLASS_EXPORT для определения GUID для каждого подкласса. Проблема: куда его поставить?

Позвольте мне сначала показать минимальный тестовый пример:

shapes.hpp

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

class Shape {
    friend class boost::serialization::access;

    template<typename Archive>
    void serialize(Archive &ar, unsigned int const version) {
        // nothing to do
    }

    public:
        virtual ~Shape() { }
};

class Rect : public Shape {
    friend class boost::serialization::access;

    template<typename Archive>
    void serialize(Archive &ar, unsigned int const version) {
        ar & boost::serialization::base_object<Shape>(*this);
    }

    public:
        virtual ~Rect() { }
};

#ifdef EXPORT_IN_HEADER
    BOOST_CLASS_EXPORT(Rect)
#endif

export.cpp

#include <boost/serialization/export.hpp>
#include "shapes.hpp"

#ifdef EXPORT_IN_OBJECT
    BOOST_CLASS_EXPORT(Rect)
#endif

main.cpp

#include <iostream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/export.hpp>
#include "shapes.hpp"

#ifdef EXPORT_IN_MAIN
    BOOST_CLASS_EXPORT(Rect)
#endif

int main() {
    Shape *shape = new Rect();
    boost::archive::text_oarchive ar(std::cout);
    ar << shape;
}

В gcc я скомпилирую их с помощью

g++ -omain main.cpp export.cpp -Wl,-Bstatic -lboost_serialization-mt -Wl,-Bdynamic -DEXPORT_IN_XXX

Здесь export.cpp может выглядеть немного глупо. В моей реальной ситуации он содержит охватывающий класс, который использует идиому PIMPL, и пытается сериализовать его (полиморфную) реализацию Shape. Важным моментом является то, что BOOST_CLASS_EXPORT может находиться в другом объектном файле, чем код, вызывающий сериализацию.

Итак, вот проблема: где использовать BOOST_CLASS_EXPORT? У меня есть три варианта, которые можно активировать с помощью макросов EXPORT_IN_XXX.

  • EXPORT_IN_MAIN работает, но это не то, что я хочу. Код, вызывающий сериализацию, не должен знать о деталях реализации класса PIMPL.

  • EXPORT_IN_OBJECT компилируется, но не работает: в результате получается boost::archive::archive_exception с сообщением unregistered void cast. Согласно документации это должно быть разрешено путем сериализации базовых классов с помощью boost::serialization::base_object, как и я, но это не помогает.

  • EXPORT_IN_HEADER даже не компилируется. Макрос BOOST_CLASS_EXPORT расширяется до специализации шаблона (который мы хотели бы быть в файле заголовка), но также и для определения его статического члена. Поэтому я получаю ошибку компоновщика в отношении multiple definition of 'boost::archive::detail::init_guid<Rect>::guid_initializer'.

Если это имеет значение, я использую g++ 4.4.3 и Boost 1.40.

4b9b3361

Ответ 1

В итоге я поместил весь код сериализации в заголовок s11n.h, который включен из файла CPP, который вызывает сериализацию. По сути, сценарий EXPORT_IN_MAIN, который я набросал выше, но с макросами BOOST_CLASS_EXPORT в другом файле.

Это работает только до тех пор, пока только один блок компиляции включает s11n.h, конечно, так что, хотя он работает пока, это не настоящее решение...

Ответ 2

Экспорт сериализации классов в документах Boost.Serialization(1.44.0) указывает следующее:


BOOST_CLASS_EXPORT в том же исходный модуль, который включает любой из Заголовки заголовков архива будут создавать код [...]

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

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
... // other archives

#include "a.hpp" // header declaration for class a
BOOST_CLASS_EXPORT(a)
... // other class headers and exports

[...] Включая BOOST_CLASS_EXPORT в сам заголовок "a.hpp", как и делать с другими параметрами сериализации затруднит или сделает невозможным следовать приведенному выше правилу относительно включение заголовков архива Вызывается BOOST_CLASS_EXPORT. Эта лучше всего решить, используя BOOST_CLASS_EXPORT_KEY в заголовке деклараций и BOOST_CLASS_EXPORT_IMPLEMENT в файл определения класса.


Ответ 3

Вы можете использовать EXPORT_IN_OBJECT, но файл, содержащий BOOST_CLASS_EXPORT, должен также содержать все файлы hpp архива, которые планируют использовать.

Это связано с тем, что макрос BOOST_CLASS_EXPORT регистрирует информацию о производном типе, которую каждый архив, по вашему мнению, использует (неявно определяется на основе того, какие архивы вы включили.)

В вашем примере используйте EXPORT_IN_OBJECT, но также добавьте #include на export.cpp.

В нашем коде мы создали archives.hpp, который содержит архивы, которые мы используем, и включаем их там, где нам нужно использовать BOOST_CLASS_EXPORT. (Таким образом, у нас есть один официальный список архивов.)

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

Ответ 5

вы можете использовать и уникальный BOOST_CLASS_EXPORT_GUID() для каждого .cpp и добавьте его только в .cpp. не .h

Ответ 6

Эта проблема сводила меня с ума, пока я не понял, что мой базовый класс не был полиморфным. Другими словами, он никогда не использовал ключевое слово "virtual" в любом месте. Потому что мне не нужно полиморфное поведение.

Вот как я его исправил:

  • Я просто нажал ключевое слово "virtual" на некоторый случайный метод в базовом классе.
  • В моем файле .cpp производного класса я добавил следующее:

    #include <boost/serialization/export.hpp>
    BOOST_CLASS_EXPORT(testnamespace::derivedclass)
    

Это все, что я должен был сделать.