状態遷移をenum classを使って書く

enum classを使って状態遷移を記述しました。特徴は以下になります。
* c++11以降
* 状態(State)をenum classを使って定義する。値を指定しているが、コーディングで意識する必要はなし。
* 遷移(Transition)をStateからこちらもenuma class を使って定義する。constexprでStateの値を元にする。
* unionを使って遷移前Stateと遷移後Stateを使って今回のTransition値を導く
* TransitionIdが決まるので、switchはenum classの要素を指定できる
# ソースコード
## main
~~~c++:main.cxx
namespace StateMachine {
  enum class State : uint8_t {
  	NoState = 0,
    Idle = 1,
    Wait = 2,
    Running = 3,
	};
	constexpr uint16_t makeTransitionId(State from, State to) {
		return static_cast<uint16_t>(from) | static_cast<uint16_t>(to) << 8;
	}
	enum class Transition : uint16_t {
		NoState_Idle = makeTransitionId(State::NoState, State::Idle),
		Idle_NoState = makeTransitionId(State::Idle, State::NoState),
		Idle_Wait = makeTransitionId(State::Idle, State::Wait),
		Wait_Idle = makeTransitionId(State::Wait, State::Idle),
		Wait_Running = makeTransitionId(State::Wait, State::Running),
		Running_Wait = makeTransitionId(State::Running, State::Wait),
		Running_Idle = makeTransitionId(State::Running, State::Idle),
	};
	union convert_u {
		struct transition_t {
			State previous;
			State current;
		} state_;
		Transition transitionId;
	};
  void dispatch(State current) {
		static convert_u transition_ = {State::NoState, State::NoState};
		transition_.state_.previous = transition_.state_.current;
		transition_.state_.current = current;
		std::cout.setf(std::ios::hex, std::ios::basefield);
		std::cout << "TransitionId:" << static_cast<uint16_t>(transition_.transitionId);
		switch (transition_.transitionId) {
		case Transition::NoState_Idle:
			std::cout << ":NoState -> Idle" << std::endl;
			break;
		case Transition::Idle_Wait:
			std::cout << ":Idle -> Wait" << std::endl;
			break;
		case Transition::Wait_Idle:
			std::cout << ":Wait -> Idle" << std::endl;
			break;
		case Transition::Wait_Running:
			std::cout << ":Wait -> Running" << std::endl;
			break;
		case Transition::Running_Idle:
			std::cout << ":Running -> Idle" << std::endl;
			break;
		case Transition::Running_Wait:
			std::cout << ":Running -> Wait" << std::endl;
			break;
		case Transition::Idle_NoState:
			std::cout << ":Idle -> NoState" << std::endl;
			break;
		default:
			std::cout << ":Undefined" << std::endl;
			break;
	  }
	  std::cout << "prev. State:" << static_cast<uint16_t>(transition_.state_.previous) << " curr. State:" << static_cast<uint16_t>(transition_.state_.current) << std::endl;
  }
}
int main(int argc, char ** argv)
{
	StateMachine::dispatch(StateMachine::State::Idle);
	std::cout << "----------------------------------------" << std::endl;
	StateMachine::dispatch(StateMachine::State::Wait);
	std::cout << "----------------------------------------" << std::endl;
	StateMachine::dispatch(StateMachine::State::Running);
	std::cout << "----------------------------------------" << std::endl;
	StateMachine::dispatch(StateMachine::State::Idle);
	std::cout << "----------------------------------------" << std::endl;
	StateMachine::dispatch(StateMachine::State::Running);
	return 0;
}
~~~
## Makefile
~~~Makefile:Makefile
TARG += enumclass
SRCS += main.cxx
OPTS += -g
FLAGS += -std=c++11 -Wno-unused-parameter -fno-strict-aliasing -fsanitize=address -fno-omit-frame-pointer
LIBDS += -L/usr/lib/llvm-10/lib -L/usr/include/lib

$(TARG): main.cxx
	clang++ $(SRCS) $(OPTS) $(FLAGS) $(INCS) $(LIBDS) $(LIBS) -o $(TARG)
~~~
# 実行結果
~~~shell-session
$ ./enumclass
----------------------------------------
TransitionId:100:NoState -> Idle
prev. State:0 curr. State:1
----------------------------------------
TransitionId:201:Idle -> Wait
prev. State:1 curr. State:2
----------------------------------------
TransitionId:302:Wait -> Running
prev. State:2 curr. State:3
----------------------------------------
TransitionId:103:Running -> Idle
prev. State:3 curr. State:1
----------------------------------------
TransitionId:301:Undefined
prev. State:1 curr. State:3
~~~