Где поставить 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.
Ответы
Ответ 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. (Таким образом, у нас есть один официальный список архивов.)
Недостатком является то, что нам нужно перестроить все, когда мы решили использовать новый тип архива, но мы обнаружили, что это намного проще в использовании, чем поддержка полиморфного архива.
Ответ 4
Проверьте этот старый поток.
http://lists.boost.org/boost-users/2005/01/9390.php
Ответ 5
вы можете использовать и уникальный BOOST_CLASS_EXPORT_GUID() для каждого .cpp
и добавьте его только в .cpp. не .h
Ответ 6
Эта проблема сводила меня с ума, пока я не понял, что мой базовый класс не был полиморфным. Другими словами, он никогда не использовал ключевое слово "virtual" в любом месте. Потому что мне не нужно полиморфное поведение.
Вот как я его исправил:
Это все, что я должен был сделать.