イベントドリブンなステートマシンライブラリを作った。
https://github.com/maueki/seedsm
特徴は以下の通り
- C++11以降
- libev上で動作
- 1ファイルをインクルードするだけ
- Qt5のステートマシンライクなイベントドリブンステートマシン
- 最低1クラス定義すれば使用可能
使用例
使用例としてQt5ステートマシンのドキュメントにある以下のステートマシンを実装してみる。
ポリシークラスの作成
まずはポリシークラスを作成。
状態をSTATE
で、イベントをEVENT
で定義する。
イベントはDEFINE_EVNET()
マクロを呼んでおく必要もある。
struct Policy {
enum STATE { s1, s2, s3 };
enum EVENT { clicked };
};
DEFINE_EVENT(Policy::clicked);
内部でログ出力に使うためto_string()
を定義しておく
std::string to_string(Policy::STATE st) {
return "s" + std::to_string(static_cast<int>(st) + 1);
}
std::string to_string(Policy::EVENT ev) {
return "clicked";
}
状態と遷移の作成
ステートマシンクラスを作成し、状態と遷移を定義する。
using ST = Policy::STATE;
using EV = Policy::EVENT;
int main(int argc, char *argv[]) {
ev::dynamic_loop main_loop;
seedsm::StateMachine<Policy> sm("Root", main_loop);
// 状態作成
sm.create_states({ST::s1, ST::s2, ST::s3});
// 遷移作成
sm.add_transition<EV::clicked>(ST::s1, ST::s2);
sm.add_transition<EV::clicked>(ST::s2, ST::s3);
sm.add_transition<EV::clicked>(ST::s3, ST::s1);
// 以下後述
これで図と同じステートマシンが定義できた。
実行
状態遷移を開始して別スレッドから1秒毎にclickedイベントを送信する
sm.start();
auto f = std::async(std::launch::async, [&sm] {
using namespace std::literals;
for (int i=0; i<5; ++i) {
std::this_thread::sleep_for(1s);
sm.send<EV::clicked>();
}
});
main_loop.run(0);
return 0;
}
ビルドして実行してみる
$ g++ -o seedsm_sample main.cpp -lev -lpthread -std=gnu++14
$ ./seedsm_sample
initialize
enter state: Root
enter state: s1
exit state: s1
enter state: s2
exit state: s2
enter state: s3
exit state: s3
enter state: s1
exit state: s1
enter state: s2
exit state: s2
enter state: s3
# press Ctrl+C
コードの全体は以下に置いた
その他の機能
-
on_state_entered()
,on_state_exited()
,on_transition()
で遷移に対するコールバック処理の登録 -
send_high()
で高優先イベント送信 -
set_parallel(true)
で子状態を並行状態にする - イベント定義に
DEFINE_EVENT_WITH_DATA()
マクロを使うことでイベント送信時にデータを付加