Boost(2)シグナルとスロットを調べる
概要 :
今回の記事は、Qtそっくりのシグナルについて記述します。
Qtのシグナルとスロットに関する記事で用いたサンプルを元に、Boostでどのように記述すると同様の効果を得るか記述してみたいと思います。
では、早速、サンプルソースを使って試してみましょう。
ここで使用するプログラムは、下記の関連サイトに記載されているサンプルプログラムを判りやすく、一部、変更したものです。
関連記事:
Qt(2)シグナルとスロットを調べる
ここで使用したサンプルソースコード:
コンソールアプリケーションで試してみましょう
簡単に動作確認するために、関連記事:
Qt(2)シグナルとスロットを調べる と同様に
コンソールアプリケーションを作成します。
※ここで用いるサンプルソースコードは、関連記事:
Qt(2)シグナルとスロットを調べる を
元に変更されたものです。
サンプルファイルを3つ準備します。
- sample.cpp
-- mainルーチンです。
- counter.h
-- テストクラスヘッダです。
- counter.cpp
-- テストクラスソースです。
では、それぞれのソースコードを見てみましょう。
[counter.h]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| #include <iostream>
#include <string>
#include <boost/bind.hpp>
#include <boost/signals.hpp>
using namespace std;
using namespace boost;
class Counter
{
public:
Counter(const string sname);
int value() const;
void clear();
//public slots:
public :
void setValue(int value) ;
void setValue2(int value) ;
//signals:
// void valueChanged(int newValue);
boost::signal<void (int)> valueChanged;
private:
int m_value;
string m_name;
};
|
コンストラクタにオブジェクト名の引数をもらうようにしました。
これにより、後述の出力情報で、何がどう動いたかわかりやすくなると思います。
※あえて、Qtの部分を一部コメントして残しています。
Qtでは、
スロットの定義は、
"public slot:"以降に記述します。
シグナルの定義は、
"signals:"以降に記述します。
しかし、Boostでは、そのような記述は必要ありません。
ただし、Qtでは、シグナルは、一つのメソッドの扱いでしたが、
Boostでは、シグナルは、一つの属性となります。(実体は、コールバック関数のかたまりのようなものですが。)
[counter.cpp]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| #include "counter.h"
Counter::Counter(const string sname)
{
m_value = 0;
m_name = sname;
}
int Counter::value() const
{
return m_value;
}
void Counter::setValue(int value)
{
if (value != m_value) {
m_value = value;
cout << "send:[" << m_name << "] " << value << endl;
// emit valueChanged(value);
valueChanged(value);
cout << "completed:[" << m_name << "] " << value << endl;
} else {
cout << "Ignore:[" << m_name << "] " << value << endl;
}
}
|
setValueのメソッドをコールしたとき、引数の数値と内部の属性に持つ数値が
異なる場合のみ
設定して、シグナルを発行するようになっています。
これにより、後述の出力情報で、何がどう動いたかわかりやすくなると思います。
引数の数値と内部の属性に持つ数値が
一致する場合は、無視します。
それぞれのルートでオブジェクト名をもらった引数を出力しています。
Qtでは、シグナルをコールする際、"emit"を慣習的に用いますが、Boostでは、何も必要ありません。
属性を関数のようにコールします。
このあたりが、Boostらしいと言えば、そのような感じもしますね。
[sample.cpp]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| #include "counter.h"
int main(int argc, char *argv[])
{
Counter a("a");
Counter b("b");
// QObject::connect(&a, SIGNAL(valueChanged(int)),
// &b, SLOT(setValue(int)));
// QObject::connect(&b, SIGNAL(valueChanged(int)),
// &a, SLOT(setValue(int)));
a.valueChanged.connect(
boost::bind(&Counter::setValue, &b, _1));
b.valueChanged.connect(
boost::bind(&Counter::setValue, &a, _1));
a.setValue(12);
b.setValue(48);
}
|
Qtでは、コネクトを行う場合、
シグナルの発生オブジェクト、シグナルメソッド、スロット受信オブジェクト、スロットメソッド
のように定義していました。
Boostでは、コネクトは、シグナルというオブジェクトへ直接、スロット関数を定義します。
ここで、スロットは関数として扱われますので、boost::bindを使って、オブジェクト+メソッドのように定義してあげることで
該当するオブジェクトへシグナルを通知できるようになります。
ここのboost::bindも、Boostの特徴であり、よく使われるものです。
boost::bindは、マニュアル等々でよく確認しておいた方が良いと思います。
※以降は、関連記事:
Qt(2)シグナルとスロットを調べる から、
このサンプルの動作説明を行っているところを抜粋しています。
さて、ソースコードの10~13行に
QObject::connectの記述がありますが、よく見てください。
a.valueChanged ->
b.setValue への流れが、10行目です。
b.valueChanged ->
a.setValue への流れが、12行目です。
a,
b.ともにそれぞれが、シグナルを発行し、受信します。
たすきがけの定義です。しかし、、
[counter.cpp]のスロット(受信側)
setValueでは、シグナルを、また、発行します。
つまり、
a.valueChanged ->
b.setValue ->
b.valueChanged ->
a.setValue となり、最後は、また先頭のシグナルを発行するわけですから、無限に処理が続きそうです。
しかし、
[counter.cpp]のスロット(受信側)
setValueでは、引数の数値と内部の属性に持つ数値が
一致する場合は、無視します。
そのため、無限ループには陥りません。
また、この処理は、よくデッドロックに陥り易い処理です。相互に呼びあう処理になっています。
オブジェクト a --> オブジェクト b
<--
さて、この実行結果はどうでしょう。思ったとおりに処理されるでしょうか。
前回の
Qtのコンソールアプリケーションの作成と同様、ここでもコンソールアプリケーションを作成し、実行してみましょう。
1
| C:\temp> qmake "CONFIG+=console"
|
以下がその実行結果です。
1
2
3
4
5
6
7
8
9
10
| send:[a] 12
send:[b] 12
Ignore:[a] 12
completed:[b] 12
completed:[a] 12
send:[b] 48
send:[a] 48
Ignore:[b] 48
completed:[a] 48
completed:[b] 48
|
きれいに動きました。スレッドでも大丈夫なように、この処理では、普通に大丈夫なようです。
(ここで試したものは、シングルタスク、シングルCPU環境下なので、本当は、もっといろんなことを試さないといけません)
ここに記載のとおり、Boost版も動作します。
このように、Boostのシグナル、スロットは、Qtのそれを置き換えられるように、ほぼなっているようです。
もともと、Qtのシグナル、スロットがモデルとなって、実装された経緯があるようでうから、当然なのかもしれませんね。
また、Boostの1.4.0では、Signal2が登場しています。サンプルには、そのSignal2も同様に、同じソースコードの中に記載しています。
是非、ご参考ください。
もっと、Boostについて詳しく知りたい方は、以下の本なども良いと思います。
本から学ぶことは多いと思います。ネットだけでは判らない様々な事に気づかされます。
コメントをどうぞ