LoginSignup
7
4

More than 5 years have passed since last update.

[C++] イベントドリブンなステートマシンライブラリを作った

Last updated at Posted at 2017-10-12

イベントドリブンなステートマシンライブラリを作った。
https://github.com/maueki/seedsm

特徴は以下の通り

  • C++11以降
  • libev上で動作
  • 1ファイルをインクルードするだけ
  • Qt5のステートマシンライクなイベントドリブンステートマシン
  • 最低1クラス定義すれば使用可能

使用例

使用例としてQt5ステートマシンのドキュメントにある以下のステートマシンを実装してみる。

statemachine

ポリシークラスの作成

まずはポリシークラスを作成。
状態を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()マクロを使うことでイベント送信時にデータを付加
7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4