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

Я пытаюсь сериализовать указатель на полиморфный класс Shape. Поэтому мне нужно использовать BOOST_CLASS_EXPORT macro для определить 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.

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

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

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

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


person Thomas    schedule 03.08.2010    source источник
comment
Вы решили эту проблему? Я сам столкнулся с этой проблемой, либо получая исключение незарегистрированного класса во время выполнения, либо ошибки boost::archive::detail::init_guid<SomeClass>::guid_initializer во время компиляции. Я очень озадачен, поэтому, если вы поняли это после того, как задали этот вопрос, я был бы очень признателен, если бы вы поделились !! Спасибо!   -  person bguiz    schedule 19.10.2010
comment
@bguiz: На самом деле не решил, нет. Смотрите мой ответ ниже.   -  person Thomas    schedule 20.10.2010


Ответы (6)


Экспорт сериализации класса Boost. В документации по сериализации (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 в файле определения класса.


person Martin Ba    schedule 04.11.2010
comment
Я поместил BOOST_CLASS_EXPORT_KEY в свой заголовок и BOOST_CLASS_EXPORT_IMPLEMENT в свой файл реализации. Я создаю библиотеку из них, а затем, когда я пытаюсь сериализовать объект в исполняемом файле, который ссылается на библиотеку, сериализатор не знает о типе. См. этот пример: chat.stackoverflow.com/transcript/message/28926778#28926778 - person David Doria; 24.02.2016
comment
@DavidDoria - извините, ничем не могу вам помочь. Давно ничего не делал с Boost.Ser - person Martin Ba; 24.02.2016

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

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

person Thomas    schedule 20.10.2010
comment
Увы, это не сработало для меня - в итоге я использовал Archive::register_type(static_cast<ClassName*>(NULL)) в методе сериализации сериализуемого центрального класса. Это сработало для меня, ч/б я не думаю, что это тот же сценарий, с которым вы столкнулись~ - person bguiz; 21.10.2010
comment
@bguiz: Ну, я думаю, это был бы аргумент против использования аббревиатуры... ;) Но я единственный, кто поддерживает этот код, так что для меня это не проблема. Экономит массу ввода. - person Thomas; 02.11.2010

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

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

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

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

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

person david    schedule 03.11.2010

Проверьте эту старую тему.

http://lists.boost.org/boost-users/2005/01/9390.php

person Tim    schedule 10.08.2010

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

person pablo madoery    schedule 27.03.2014

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

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

  1. Я только что применил ключевое слово «виртуальный» к какому-то случайному методу в своем базовом классе.
  2. В файле .cpp моего производного класса я добавил следующее:

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

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

person Todd    schedule 07.08.2014