2009-09-08 17 views
9

私は次の状況があります:スタンドアロンスタティックライブラリでウィジェットを作成し、最終アプリケーション(Visual C++ 9.0、qt 4.5)にリンクする必要があります。 この静的ウィジェットライブラリにはいくつかのリソース(アイコン)が含まれており、複数の.cppファイルで構成されています(それぞれスタンドアロンウィジェットを含みます)。私が知る限り、Qtリソースシステムを静的ライブラリで使用する場合は、Q_INIT_RESOURCE(resource_file_name)を呼び出して初期化する必要があります。私は(静的ライブラリ内のすべての.cppファイルに)次のコードでこれを解決:静的ライブラリに埋め込まれたQtリソースを初期化する

 

#include <QAbstractButton> 

namespace { 
struct StaticLibInitializer 
{ 
    StaticLibInitializer() 
    { 
     Q_INIT_RESOURCE(qtwidgets_custom_resources); 
    } 
}; 
StaticLibInitializer staticLibInitializer; 
} 

// ... widget code .... 
 

代わりに私の最初のアプローチで、私は初期化を含む避けるために、初期化コードの静的ライブラリプロジェクト(に別のinit.cppファイルを作成しましたすべての.cppファイル内のコード)、これは機能しませんでした。

これはなぜ機能しませんでしたか?

StaticLibInitializerでのこのアプローチは、さまざまなコンパイラやプラットフォーム間で安全で移植性がありますか?

答えて

10

static initialization order fiascoでヒットしたために動作しませんでした。

静的オブジェクトを初期化するコードを移動することはできません。これらの静的オブジェクトが使用されている翻訳単位(ソースファイルとして読むことができます)を超過します。あなたのやり方ではありません。これらの静的オブジェクトを初期化するために使用しているスキームを使用する場合は、宣言のみをinit.hppヘッダーに移動するが、静的オブジェクトを使用する各ファイルにはStaticLibInitializer staticLibInitializer;という記述を残してください。
上記のアドバイスは、各ウィジェットが独自のリソースのみを使用していることを前提としています。あるウィジェットのリソースが別のウィジェットによって使用されている状況がある場合は、静的初期化オーダーの失敗を再度実行します。あなたは一度だけ与えられたリソースを初期化しますStaticLibInitializerの確認乗算のインスタンスを作成し、次にあなたが与えられた翻訳単位で使用しようとしているすべてのリソースのためのStaticLibInitializerをインスタンス化するために、この

StaticLibInitializer 
{ 
    void initialize() 
    { 
     static Q_INIT_RESOURCE(qtwidgets_custom_resources); 
    } 

    StaticLibInitializer() 
    { 
     initialize(); 
    } 
} 

のようなコードを使用することによって、このような状況を管理することができます。

+0

私の現在の状況で私は3つの.cppファイルを持っています(それぞれは独自のウィジェットを実装していますが、そのうち2つは.qrcファイルのリソースを使用します)が、元の質問では、良い(100%、50/50ではない)。だから私は理解できません、なぜ私は別のinitに初期化コードを入れます。cppファイル私は自分のリソースを使用することはできませんが、ウィジェットの.cppファイルのいずれかにあるこのコードがすべてうまくいきます... – cybevnm

+0

これは問題なく動作します** ** ** **)別のコンパイラーや同じコンパイラーの別のバージョンを使用して作業を開始すると、作業が停止する可能性があります。それは**定義されていない動作**です。その理由は、ウィジェットのファイルのいずれかに初期化コードがあると、まずコンパイルが行われ、リソースを最初に初期化するからです。純粋な運が、それ以上はありません。あなたのプログラムが晴れた日に0%働かないようにするには、静的な初期化の失敗を避けるための指示に従ってください。 –

+0

コンパイル時にコンパイラによって静的初期化の順序が定義されているか、プログラム間で順序が変わることがあります(再コンパイルなし)。 – cybevnm

6

Q_INIT_RESOURCEマクロは名前空間で使用できません。

qtマニュアルから引用してください: "注意:このマクロは名前空間では使用できません。main()"から呼び出す必要があります。でも、それはあなたにこれが不可能な場合は、右のそれを行う方法の例を示します:

inline void initMyResource() { Q_INIT_RESOURCE(myapp); } 

    namespace MyNamespace 
    { 
    ... 

    void myFunction() 
    { 
     initMyResource(); 
    } 
    } 

なぜ、どのように正確にそれが失敗したり、不特定の方法でそれを使用する場合、失敗しない自分を見てください。関連するコードはQtCoreにあります。

+0

しかし、(静的ライブラリのすべての.cppファイルにコードを含めると)これは(匿名の​​名前空間でも)機能します。 – cybevnm

+0

上記の '' inline'を使用しても、コンパイラによって尊重される保証はありませんので、何も購入しません。 *このキーワードを尊重しないことは、C++標準に準拠しています。したがって、この*ソリューション*が前提に基づいている場合、インライン関数はインライン展開されます。 –

+1

'inline'関数は、特にODRに関しては、わずかに異なるセマンティクスを持ちます。私たちがすべてのプラットフォームで 'Q_INIT_RESOURCE'のマクロ展開を知らないことを考えれば、それが必要かどうか判断するのは難しいです。確かにそこに置くのが妥当です。 – MSalters

関連する問題