C++を学び、いくつかのパターンに慣れようとしています。 signals2 doc には、スロットとシグナルを使用して実行できるさまざまなことが明らかにあります。私が理解していないのは、どのタイプのアプリケーション(ユースケース)に使用すべきかということです。
変更イベントをディスパッチするステートマシンの方針に沿って考えています。動的に型指定されたバックグラウンド(C#、Javaなど)から来る場合は、イベントディスパッチャーまたは静的参照またはコールバックを使用します。
クラス間コールバックの使用に関してC++に問題はありますか?それは本質的になぜsignals2が存在するのですか?
例の1つは、ドキュメント/ビューです。このパターンは、関数のベクトルを使用してループ内でそれぞれを呼び出す、または登録されたリスニングクラスインスタンスの状態変化を呼び出すラムダを言うよりも、どのように適していますか?
class Document
{
public:
typedef boost::signals2::signal<void ()> signal_t;
public:
Document()
{}
/* Connect a slot to the signal which will be emitted whenever
text is appended to the document. */
boost::signals2::connection connect(const signal_t::slot_type &subscriber)
{
return m_sig.connect(subscriber);
}
void append(const char* s)
{
m_text += s;
m_sig();
}
const std::string& getText() const
{
return m_text;
}
private:
signal_t m_sig;
std::string m_text;
};
そして
class TextView
{
public:
TextView(Document& doc): m_document(doc)
{
m_connection = m_document.connect(boost::bind(&TextView::refresh, this));
}
~TextView()
{
m_connection.disconnect();
}
void refresh() const
{
std::cout << "TextView: " << m_document.getText() << std::endl;
}
private:
Document& m_document;
boost::signals2::connection m_connection;
};
Boost.Signals2
は単なる「コールバックの配列」ではなく、多くの付加価値があります。 IMO、最も重要なポイントは次のとおりです。
connection
およびscoped_connection
ハンドルは、signal
に直接アクセスせずに切断できるようにします。これが、boost::function
(またはstd::function
)のような比類のないスロットを切断する唯一の方法であることに注意してください。自動スロット寿命追跡:信号は「期限切れ」スロットから自動的に切断されます。スロットがshared_ptr
sによって管理されているコピー不可能なオブジェクトを参照するバインダーである場合の状況を考えてみましょう。
shared_ptr<listener> l = listener::create();
auto slot = bind(&listener::listen, l.get()); // we don't want aSignal_ to affect `listener` lifespan
aSignal_.connect(your_signal_type::slot_type(slot).track(l)); // but do want to disconnect automatically when it gets destroyed
確かに、「関数のベクトルを使用し、ループ内でそれぞれを呼び出す」など、上記のすべての機能を自分で再実装できますが、問題は、Boost.Signals2
よりも優れているかどうかです。車輪の再発明が良い考えになることはめったにありません。