在boost::serialization中,BOOST_CLASS_EXPORT应该放在哪里?

12
我正在尝试将一个指向多态类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

导出.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 展开为一个模板特化(我们希望它在头文件中),但也展开为其中的静态成员定义。因此,我会得到关于 boost::archive::detail::init_guid<Rect>::guid_initializer 的多重定义链接器错误。

如果有影响的话,我正在使用 g++ 4.4.3 和 Boost 1.40。


你解决了这个问题吗?我自己也遇到了这个问题,无论是在运行时得到未注册类异常还是在编译时得到boost::archive::detail::init_guid<SomeClass>::guid_initializer错误。 我感到相当困惑,所以如果你已经解决了这个问题,我会非常感激你的分享!谢谢! - bguiz
@bguiz:并没有真正解决它,可以看下面我的回答。 - Thomas
6个回答

10

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

我已经在我的头文件中放置了BOOST_CLASS_EXPORT_KEY,而在我的实现文件中放置了BOOST_CLASS_EXPORT_IMPLEMENT。我从这些构建了一个库,然后当我尝试在链接到该库的可执行文件中序列化对象时,序列化程序不知道该类型。请参见此示例:http://chat.stackoverflow.com/transcript/message/28926778#28926778 - David Doria
@DavidDoria - 抱歉,我无法帮助你。很长时间没有使用Boost.Ser了。 - Martin Ba

4

我最终将所有序列化代码放在了一个名为s11n.h的头文件中,该文件从调用序列化的CPP文件中包含。本质上,这与我上面草拟的EXPORT_IN_MAIN场景相同,但是BOOST_CLASS_EXPORT宏调用在不同的文件中。

当然,只要有一个编译单元包含了s11n.h,这种方法就可以工作,所以尽管现在它能够工作,但并不是真正的解决方案...


唉,这对我没用 - 最终我做的是在被序列化的中心类的序列化方法中使用Archive::register_type(static_cast<ClassName*>(NULL))。这对我有用,但我认为这不是你遇到的情况。 - bguiz
@bguiz:好吧,我猜这就是反对使用缩写的一个论点... ;) 但是只有我在维护这段代码,所以这对我来说不是问题。可以省去很多打字时间。 - Thomas

2

你可以使用 EXPORT_IN_OBJECT,但包含 BOOST_CLASS_EXPORT 的文件还必须包括所有打算使用的档案hpp文件。

这是因为 BOOST_CLASS_EXPORT 宏会向每个它认为你将使用的档案注册派生类型信息(隐式地基于你所包括的档案)。

在你的示例中,使用 EXPORT_IN_OBJECT,但也要将 #include 添加到 export.cpp 中。

在我们的代码中,我们创建了一个包含我们使用的档案的 archives.hpp,并在需要使用 BOOST_CLASS_EXPORT 的任何地方包含它。(这样我们就有了一个单一的正式档案列表。)

缺点是当我们决定使用新的档案类型时,我们需要重新构建所有内容,但我们发现这比多态档案支持更容易使用。



1
这个问题让我疯狂,直到我意识到我的基类不是多态的。换句话说,它从未在任何地方使用关键字“virtual”。因为我不需要多态行为。
以下是我的解决方法:
我只是在我的基类中的某个随机方法上添加了关键字“virtual”。 在我的派生类的.cpp文件中,我添加了以下内容: #include BOOST_CLASS_EXPORT(testnamespace::derivedclass)

这就是我所要做的一切。


0

你可以在每个 .cpp 文件中使用唯一的 BOOST_CLASS_EXPORT_GUID(),并且只需将其添加到 .cpp 文件中,而不是 .h 文件中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接