web-dev-qa-db-ja.com

C ++グローバル変数は、静的ライブラリを介してリンクされている場合は初期化されませんが、ソースでコンパイルされている場合は問題ありません

グローバルインスタンスのコンストラクターに基づいて、関数オブジェクト(ファンクター)をマップに自動的に登録するシステムを作成しました。

ファンクターを定義する各cppファイルには、ファンクターをシングルトンstd::map<int, std::function<...> >オブジェクトに登録するためのレジストラクラスインスタンスのグローバルインスタンスがあります。

これは、レジストラクラスの定義です。

template
<
    typename map_type,
    typename handler_type
>
struct registrar
{
    registrar
        (
             map_type& map_object,
             boost::uint16_t cmd_code,
             const handler_type& handler
        )
        {
          map_object.insert(std::pair<boost::uint16_t, handler_type>(cmd_code, handler));
        }
};

各.cppファイル内。グローバルインスタンスは次のように定義されます。

namespace one_way
{
    static registrar <in_out_map_type, handler>
        post_receiver(in_out_map_type::instance(), command, handlers());
}

すべてのcppをmain.cppと一緒にコンパイルすると、すべて正常に動作します。しかし、cppファイルを静的ライブラリにコンパイルしてmain.cppにリンクすると、登録が機能しません。

WindowsとUbuntu11.10の両方でVC10とGCC4.61を使用してテストしました。両方とも失敗します。

私は 同じ問題のスレッド を見つけましたが、OPは彼がそれを解決したかどうかを言いませんでした。

私は何かが足りませんか?


編集


コメントを含むすべての回答に感謝します。

すべての回答は確かに私がこの方法をもっと考え、深く調査するのに役立ちました。すべての研究と試行の後、私は最終的に、バイナリ境界を越えて自己登録のためにグローバル/静的変数に依存するという考えをあきらめました、それが機能することを保証するportable方法がないため。

私の最後の方法は、登録を1つのバイナリ内に保持することです。

22
t.g.

Android NDKの動作、この問題の影響を受ける静的ライブラリはLOCAL_WHOLE_STATIC_LIBRARIES変数に追加する必要があります-それらは_-Wl,--whole-archive_フラグを使用して参照され、勝ちますストリッピングの対象にはなりません。

MSVCの長い答え:

翻訳ユニットの静的変数は、翻訳ユニットの通常のコードが実行される前に初期化されます。実際には、初期化は、含まれている実行可能ファイルまたはダイナミックライブラリがロードされたときに行われます。\c main()が呼び出されるか、LoadLibrary()/dlopen()の呼び出しが完了すると、静的変数が初期化されます。

[〜#〜] msdn [〜#〜] で説明されている問題:

コンストラクターと、宣言内のグローバル関数または静的メソッドによる割り当ては、参照を作成せず、/ OPT:REFの削除を妨げません。そのようなコードからの副作用は、データへの他の参照が存在しない場合に依存すべきではありません。

複数の変換ユニットからのオブジェクトコードを単一のファイルに配置すると便利な場合があります。静的ライブラリは、従来は\ c.libまたは\ c.aサフィックスで名前が付けられていました。 MSVCリンカーは静的ライブラリの依存関係分析を行い、含めるエンティティによって参照されないコードは含まれません。

静的変数を使用してファクトリオブジェクトの登録を宣言および発生させる一般的なパターンは、この状況では失敗する可能性があります。MSVCリンカーは、静的変数を到達不能と見なし、結果から削除します。

ソリューション

便利なグーグル検索: http://www.google.com/search?q=msvc+factory+static+library

1つの解決策は、インクルードエンティティに_/OPT:NOREF_リンカーフラグを設定することです。ただし、これはオールオアナッシング設定であり、含まれているすべてのライブラリが完全にリンク可能である必要があります。

スタティックを含むファイル内の何かがインクルードエンティティによって(直接的または間接的に)参照されている場合、言語ルールによってスタティック自体を保持する必要があります。

最も基本的なアプローチは、ダミー関数をファイルに配置し、到達可能と見なされることがわかっている場所からそれを参照することです。

別のアプローチは、_/INCLUDE_リンカーフラグを使用して、問題ファイル内のエンティティを参照することです。 DummyForLinkProblemという名前のエンティティを想定すると、これはインクルードエンティティのソースで実行できます。

_#pragma comment(linker, "/include:DummyForLinkProblem")
_

ZooLibのソリューション

現在この問題の影響を受けているZooLibエンティティは、ZFile_Win.cpp、ZGRgnRep_HRGN.cpp、ZNet_Internet_WinSock.cpp、ZStreamRWCon_SSL_Win.cpp、ZTextCoder_Win.cpp、およびZUnicode_Normalize_Win.cppにあるエンティティです。

対応するヘッダーファイルに_#include ZCompat_MSVCStaticLib.h_を入れ、それぞれにZMACRO_MSVCStaticLib_Reference(ModifiedFileName)を入れます。 cppファイルにZMACRO_MSVCStaticLib_cpp(ModifiedFileName)を入れます。 ModifiedFileNameは通常、先頭のZとファイル拡張子が削除されたファイル名であり、_ZCONFIG_API_XXX_マクロで使用されるのと同じスタイルです。

実行可能ファイルまたはライブラリがこれらのエンティティを削除しないようにするには、インクルードエンティティの既知の参照コードから適切なヘッダーファイルを#includeするだけです。これにより、実行されない参照が発生し、期待どおりに機能します。

9
Andrew Green