enum classを使って状態遷移を記述しました。特徴は以下になります。
- c++11以降
- 状態(State)をenum classを使って定義する。値を指定しているが、コーディングで意識する必要はなし。
- 遷移(Transition)をStateからこちらもenuma class を使って定義する。constexprでStateの値を元にする。
- unionを使って遷移前Stateと遷移後Stateを使って今回のTransition値を導く
- TransitionIdが決まるので、switchはenum classの要素を指定できる
ソースコード
main
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
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)
実行結果
$ ./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