さまざまな問題を一度に説明しています。ある種の静的初期化を行うという特定の問題については、登録を実行する偽のクラスを作成する簡単な方法があります。次に、さまざまなクラスのそれぞれがstatic const X
メンバーを持つことができます。メンバーは翻訳単位で定義する必要があり、定義はインスタンスのインスタンス化とクラスの登録をトリガーします。
これは、初期化順序の大失敗という難しい問題に取り組んでいません。言語は、異なる翻訳単位でのオブジェクトの初期化の順序について保証しません。つまり、このようなクラスを使用して 3 つの翻訳単位をコンパイルした場合、偽のメンバーの相対的な実行順序は保証されません。これはライブラリにも適用されます。クラスを登録するコンテナーがグローバル/静的メンバー属性である場合、そのコンテナーが初期化されているという保証はありません。
コードにアクセスできる場合は、コンテナー コードを変更して を使用できstatic local variables
ます。これは、初期化の順序を確実にするための一歩です。可能な解決策のスケッチとして:
// registry lib
class registry { // basically a singleton
public:
static registry& instance() { // ensures initialization in the first call
static registry inst;
return inst;
}
// rest of the code
private:
registry(); // disable other code from constructing elements of this type
};
// register.h
struct register {
template <typename T>
register( std::string name ) {
registry::instance().register( name, T (*factory)() ); // or whatever you need to register
}
};
// a.h
class a {
public:
static a* factory();
private:
static const register r;
};
// a.cpp
const register a::r( "class a", a::factory );
// b.h/b.cpp similar to a.h/a.cpp
この場合、a
およびb
クラスの登録に明確な順序はありませんが、問題にはならない可能性があります。一方、関数でローカル静的変数を使用することにより、registry::instance
シングルトンの初期化は、メソッドへの呼び出しの前にregistry::register
(メソッドへの最初の呼び出しの一部としてinstance
) 実行されることが保証されます。
その変更を行うことができない場合は、基本的に不運でありregistry
、他の翻訳単位の他の静的メンバー属性 (またはグローバル) の前にインスタンス化されることを保証できません。その場合、クラスの登録を最初のインスタンス化まで延期し、登録する各クラスのコンストラクターにコードを追加して、オブジェクトの実際の構築前にクラスが登録されるようにする必要があります。
他のコードがその型のオブジェクトを作成するかどうかによって、これが解決策になるかどうかが決まります。ファクトリ関数の特定のケース (最初に頭に浮かんだもの) では、型のオブジェクトを作成することが他に何も許可されていない場合、a
またはb
... コンストラクター呼び出しでピギー バッキング登録を行うことも解決策にはなりません。