イベント処理などで使われるシグナル/スロットには、Boost Signals2 Libraryを使用する。
Boost.Signals2のシグナルには、connect()
関数によって、複数の関数を接続することができる。
シグナルの関数呼び出し演算子によって、接続した関数全てを呼び出すことができる。
#include <iostream>
#include <boost/signals2/signal.hpp>
#include <boost/bind.hpp>
struct Point {
int x, y;
Point(int x, int y) : x(x), y(y) {}
};
class Button {
public:
boost::signals2::signal<void(const Point&)> clicked;
void click()
{
clicked(Point(10, 10)); // 呼び出し
}
};
class MainView {
public:
void on_clicked(const Point& p)
{
std::cout << "MainView : clicked" << std::endl;
}
};
class SubView {
public:
void on_clicked(const Point& p)
{
std::cout << "SubView : clicked" << std::endl;
}
};
int main()
{
MainView mainView;
SubView subView;
Button button;
// クリックイベントの登録
button.clicked.connect(boost::bind(&MainView::on_clicked, &mainView, _1));
button.clicked.connect(boost::bind(&SubView::on_clicked, &subView, _1));
// クリックした
button.click();
}
実行結果:
MainView : clicked
SubView : clicked
スロットを切断するには、connect()
関数の戻り値であるコネクションを保持しておき、コネクションのdisconnect()
メンバ関数を呼び出すことで、切断する。
#include <iostream>
#include <boost/signals2/signal.hpp>
#include <boost/bind.hpp>
struct Point {
int x, y;
Point(int x, int y) : x(x), y(y) {}
};
class Button {
public:
boost::signals2::signal<void(const Point&)> clicked;
void click()
{
clicked(Point(10, 10));
}
};
class MainView {
public:
void on_clicked(const Point& p)
{
std::cout << "MainView : clicked" << std::endl;
}
};
class SubView {
public:
void on_clicked(const Point& p)
{
std::cout << "SubView : clicked" << std::endl;
}
};
int main()
{
MainView mainView;
SubView subView;
Button button;
// クリックイベントを登録
button.clicked.connect(boost::bind(&MainView::on_clicked, &mainView, _1));
boost::signals2::connection con =
button.clicked.connect(boost::bind(&SubView::on_clicked, &subView, _1));
// SubViewのスロットを切断
con.disconnect();
// クリックした
button.click();
}
実行結果:
MainView : clicked
特に指定しなければ、シグナルを呼び出した時の戻り値は接続する関数の戻り値のoptional
となり、最後に登録した関数の戻り値が戻される。また、関数が接続されていなければ無効値を戻す。
#include <iostream>
#include <boost/signals2/signal.hpp>
int add(int x, int y)
{
return x + y;
}
int minus(int x, int y)
{
return x - y;
}
int multiply(int x, int y)
{
return x * y;
}
int divide(int x, int y)
{
return x / y;
}
int main()
{
boost::signals2::signal<int(int, int)> sig;
boost::signals2::signal<int(int, int)> non_connect_sig;
sig.connect(&add);
sig.connect(&minus);
sig.connect(&multiply);
sig.connect(÷);
// boost::optional<int>が戻ってくる
std::cout << *sig(10, 2) << std::endl;
// 無効値
const boost::optional<int> non_connect_result = non_connect_sig(10, 2);
if(!non_connect_result) {
std::cout << "invalid value" << std::endl;
}
}
実行結果:
5
invalid value
boost::signals2::signal
クラスの2番目のテンプレート引数Combiner
を変更することで、戻り値のカスタムが可能である。
#include <iostream>
#include <numeric>
#include <boost/signals2/signal.hpp>
// 接続された複数の関数の戻り値の合計を戻す
template <typename T>
struct custom_result_value
{
typedef T result_type;
template <typename InputIterator>
result_type operator()(InputIterator first, InputIterator last) const
{
if( first == last ) {
return result_type();
}
return std::accumulate(first, last, 0);
}
};
int add(int x, int y)
{
return x + y;
}
int minus(int x, int y)
{
return x - y;
}
int multiply(int x, int y)
{
return x * y;
}
int divide(int x, int y)
{
return x / y;
}
int main()
{
boost::signals2::signal<int(int, int), custom_result_value<int> > sig;
sig.connect(&add);
sig.connect(&minus);
sig.connect(&multiply);
sig.connect(÷);
std::cout << sig(10, 2) << std::endl;
}
- custom_result_value[color ff0000]
実行結果:
45
スマートポインタで管理されたオブジェクトのメンバ関数を登録する場合、boost::signals2::signal::slot_type::track
を用いることで、オブジェクトが破棄されるタイミングで自動的に切断させることができる。
参照: Automatic Connection Management (Intermediate) - Boost Signals 2 Library
#include <iostream>
#include <boost/signals2/signal.hpp>
#include <boost/make_shared.hpp>
class Fuga{};
class Hoge
{
public:
void Func(const Fuga&){
std::cout << "Hoge::func()" << std::endl;
}
};
int main(){
typedef boost::signals2::signal<void(const Fuga&)> signal_type; // signal型
signal_type sig; // signal オブジェクト
const Fuga fuga; // 引数用
{
std::cout << "block start." << std::endl;
// shared_ptrで管理しているオブジェクトのメンバ関数を登録
// signal::slot_typeはbindのように引数の部分適用が可能.
// signal::slot_type::trackにスマートポインタを渡す事で
// オブジェクトが破棄されるタイミングで切断される
boost::shared_ptr<Hoge> hoge = boost::make_shared<Hoge>();
sig.connect(
signal_type::slot_type(&Hoge::Func, hoge.get(), _1).track(hoge)
);
sig(fuga); // 登録した関数が正しく呼ばれる
std::cout << "block end." << std::endl;
} // ここで hoge が管理するオブジェクトが破棄され、自動的にsigから切断される
sig(fuga); // 切断後なので何もしない
}
実行結果:
block start.
Hoge::func()
block end.