Boost.Serializationを使ったコードにおいて、Boostのバージョンを上げるとリンクエラーに遭遇することがあります。なぜかというと、BOOST_CLASS_EXPORTが原因です。

今のバージョン(1.45以降?)では、BOOST_CLASS_EXPORTは、2つのマクロに分割されたそうなのです。おそらく、BOOST_CLASS_EXPORTではOne definition rule (ODR)に違反する場合があるため、やむなくこのような変更をすることになったのではないかと思います。

  • BOOST_CLASS_EXPORT_KEY(ヘッダに書く)
  • BOOST_CLASS_EXPORT_IMPLEMENT(適当な1つの翻訳単位(≒.cppファイル)に書く)

いかにもマクロ内で変数か関数の宣言・定義を行なっていそうな感じですね。実際、BOOST_CLASS_EXPORTを使ったときに遭遇するリンカエラーとは、変数の重複定義です。

公式のドキュメントでは、Serialization – Special Considerationsの“Exporting Class Serialization”の箇所に書いてあります。

なお、実装を覗いたところ、BOOST_CLASS_EXPORTは、BOOST_CLASS_EXPORT_KEYしてBOOST_CLASS_EXPORT_IMPLEMENTするのと同様の内容に展開されるマクロとなっています。上の文書でも書いてあったと思いますが、1モジュールで済む場合はBOOST_CLASS_EXPORTを使い続けて問題ないわけです。自分の場合は、静的ライブラリだったためその方法は無理でした。

詳しくは、その公式の文書を読んでいただければきちんと書いてありますが、BOOST_CLASS_EXPORT_IMPLEMENTあるいはBOOST_CLASS_EXPORTは<boost/archive/*>のインクルードより後に書く必要がある点はなんだか注意が必要そうです。

この問題は、ActiveBasic(コンパイラ・IDE)で遭遇しました。ググったところ、Boost users’ mailing page: Re: [Boost-users] [boost] [serialization] Braking change with BOOST_CLASS_EXPORTを見つけ、それを基にググって先ほど挙げた公式の文書に辿り着き、解決しました。


例としてやってみましょう。次のコードをBoost 1.45を使ってコンパイル・リンクするとエラーになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Hoge.h
#ifndef HOGE_H
#define HOGE_H
 
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/export.hpp>
 
class Hoge
{
    /* …… */
private:
    friend class boost::serialization::access;
    template<typename Archive>
    void serialize(Archive& ar, unsigned)
    {
        /* …… */
    }
};
 
BOOST_CLASS_EXPORT(Hoge);
 
#endif // HOGE_H
1
2
// Foo.cpp
#include "Hoge.h"
1
2
// Bar.cpp
#include "Hoge.h"

Visual C++ではこんなリンクエラーでした。

Bar.obj : error LNK2005: "public: static struct boost::archive::detail::extra_detail::guid_initializer<class Hoge> const & const boost::archive::detail::extra_detail::init_guid<class Hoge>::g" (?g@?$init_guid@VHoge@@@extra_detail@detail@archive@boost@@2ABU?$guid_initializer@VHoge@@@2345@B) は既に Foo.obj で定義されています。

では、最初に書いたようにBOOST_CLASS_EXPORT_KEYとBOOST_CLASS_EXPORT_IMPLEMENTへ書き直します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Hoge.h
#ifndef HOGE_H
#define HOGE_H
 
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/export.hpp>
 
class Hoge
{
    /* …… */
private:
    friend class boost::serialization::access;
    template<typename Archive>
    void serialize(Archive&amp; ar, unsigned)
    {
        /* …… */
    }
};
 
BOOST_CLASS_EXPORT_KEY(Hoge);
 
#endif // HOGE_H
1
2
3
// Foo.cpp
#include "Hoge.h"
BOOST_CLASS_EXPORT_IMPLEMENT(Hoge);
1
2
// Bar.cpp
#include "Hoge.h"

これで、BOOST_CLASS_EXPORTにまつわるエラーはなくなりました。ただし、余談ですが、実際にはこのコードのままではmain関数がないというリンクエラーになります(当然ですね)。


2011年5月13日:出力結果がきちんと表示されていなかったので修正しました。

2012年3月17日:BOOST_CLASS_EXPORTによるODR違反の可能性を追記しました。


スポンサード リンク

この記事のカテゴリ

  • ⇒ Boost.Serializationを使ったコードでリンクエラーになったとき