クラスの各サブクラスに信号を送信したいと思います。 これを達成するために、私はboostからsignals2ライブラリを使用しています。コードの使用のために、私はCRTPを使用する必要があります。必要な副作用を伴うスタティック初期化が最適化されました
ヘッダ・ファイルに含まれている最小例:サブクラスに通知したい
class A {
static boost::signals2::signal<void()>& getSignal() {
static boost::signals2::signal<void()> signal;
return signal;
}
public:
static void sendSignal() {
getSignal()();
}
protected:
template <typename slot_t>
static boost::signals2::connection addSlot(slot_t slot) {
return _entityToBeDeleted().connect(slot);
}
};
template <typename concrete_B>
class B : A {
static boost::signals2::connection _installHandler() {
return A::addSlot([]() {
//Do something
});
}
static const boost::signals2::connection connection;
};
template<typename concrete_B>
const boost::signals2::connection B<concrete_B>::connection = _installHandler();
class C : public B<C> {
//Concrete subclass
};
一部のクライアント・クラスは、信号に接続されているすべてのスロットを呼び出すA::sendSignal
への呼び出しを行います。この通知の内容は、この質問の目的とは無関係です。ご覧のとおり、静的イニシャライザの副作用はB<concrete_B>::connection
です。これは動作するために必要ですが、オブジェクト自体が使用されない(必要ではない)ため、完全な初期化はスキップされます。
問題は次のとおりです。_installHandler()の呼び出しは、コンパイラによって完全に最適化されています(私はVisual Studio 2017でMSVCを使用しています)。私は接続オブジェクトが有効かどうかを明示的にテストする追加のテストケースを作成してこれを検証しました。この方法でオブジェクトを使用したとき、コードは機能し、他のすべてのテストケースは成功しました。追加のテストケースを削除すると、他のテストケースが再び失敗しました。
私はthis questionへの答えに示唆されているように静的変数volatileを宣言しようとしましたが、それは役に立ちませんでした。
最適化する静的接続オブジェクトの初期化を回避する方法を教えてください。または:テンプレートインスタンシエーションで作成された副作用のある関数を確実にするには、返されたオブジェクトが決して実際に使用されていないにもかかわらず、実行可能ファイルの先頭で必ず1回呼び出されます。理想的には静的な初期化中です。
が、私はこのクラスの別の静的初期化子で何とか(std::cout << connection.connected() << '\n';
またはこのようなもの)(これは初期化オブジェクトが確実に使用されているため)と呼ばれることがすでに安全である接続を使用して考えたが、これは厄介なように思えますハック。
編集:私は「ハック」を実装していて、完璧に機能しますが、「クリーン」な方法で目的の動作を実装する方法があれば興味があります。
いくつかのリンカーは、実行可能ファイル内のオブジェクトファイルを含めスキップする場合は他のコードないODR-使用して、そのオブジェクトファイルで定義されたものを... – aschepler
最小限の例では、少なくともヘッダーファイルの使用方法を含める必要があります。これを読む:[mcve] –
@aschepler私はそれを認識しています。なぜなら、静的初期化の関連部分が返されたオブジェクト自体ではなく(必要ではない)、関数の副作用(つまり、 'signal :: connect'の呼び出し。 – LukeG