Boost.MSMをPimplイディオムと組み合わせることで、外部に隠蔽しつつ、privateな空間にもアクセス可能なクラス内有限状態マシンを実装する。
ヘッダファイルにBoost.MSMを使用している痕跡を残さずに、有限状態マシン内で基本オブジェクト(ここではFooオブジェクト)に自由にアクセスすることができる。
Foo.h
#pragma once
#include <memory>
struct Foo
{
Foo();
~Foo();
void changeState();
private:
struct Impl;
std::unique_ptr<Impl> pImpl;
};
Foo.cpp
#include <iostream>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/back/state_machine.hpp>
struct Foo::Impl
{
// *** ポイント1
// Implクラスのクラス内クラスとしてBoost.MSM用クラスを定義する
// 状態の定義
struct RunState : boost::msm::front::state<>{
// Fooクラスのprivate変数にアクセスしている。
template<typename Event, typename FSM>
void on_entry(Event event, FSM fsm)
{
std::cout << "Run : " << ++(fsm.base->pImpl->value) << std::endl;
// std::cout << "Run : " << ++(value) << std::endl; // Foo::Impl::valueには直接アクセスできない
}
};
struct StopState : boost::msm::front::state<>{
template<typename Event, typename FSM>
void on_entry(Event event, FSM fsm)
{
std::cout << "Stop : " << ++(fsm.base->pImpl->value) << std::endl;
}
};
// イベントの定義
struct ChangeEvent {};
// 有限状態マシンの定義
struct Machine_ : boost::msm::front::state_machine_def<Machine_>
{
// 状態遷移テーブル
struct transition_table : boost::mpl::vector
< _row< RunState, ChangeEvent, StopState >
, _row< StopState, ChangeEvent, RunState >
> {};
// 初期状態
using initial_state = StopState;
// *** ポイント2
// 有限状態マシンの定義内で基本オブジェクト(Fooクラスのオブジェクト)を保持する
// 基本オブジェクトのポインタを保持。ここから基本オブジェクトにアクセスする
Foo*const base;
// 有限状態マシンのコンストラクタ
Machine_(Foo*const obj) : base(obj){}
};
// 有限状態マシンクラスの定義
using Machine = boost::msm::back::state_machine< Machine_ >;
Machine machine; // 有限状態マシンをImplクラス内で保持する(包含)
Impl(Foo*const obj)
: machine(obj)
, value(0)
{
}
// プライベート変数
int value;
};
Foo::Foo()
:pImpl(new Foo::Impl(this))
{
pImpl->machine.start();
}
Foo::~Foo()
{}
void Foo::changeState()
{
pImpl->machine.process_event(Impl::ChangeEvent());
}
main.cpp
#include "Foo.h"
int main()
{
Foo foo;
foo.changeState();
foo.changeState();
return 0;
}
result
Stop : 1
Run : 2
Stop : 3