# State transitions in base baseディレクトリを埋めようの会。 本日は[StateTransitions](https://source.chromium.org/chromium/chromium/src/+/main:base/state_transitions.h;l=59;drc=e4622aaeccea84652488d1822c28c78b7115684f)。 ## StateTransitions とは 完全順序のState集合に対して、そのtransitionをhandleする型。 正しいtransitionを登録することができ、それ以外の間違ったTransitionをするとinvalidであることを教えてくれる。 例えば以下のような感じ。 ```cpp= enum class State { kState1 = 0, kState2, kState3, kState4 }; // 1 -> {2 or 3}, 2 -> {3 or 4} にのみ推移してOK const StateTransitions<State> transitions({ {State::kState1, {State::kState2, State::kState3}}, {State::kState2, {State::kState3, State::kState4}}, }); // OK: 1つめに合致 ASSERT_TRUE(transitions.IsTransitionValid(State::kState1, State::kState2)); // OK: 2つめに合致 ASSERT_TRUE(transitions.IsTransitionValid(State::kState2, State::kState3)); // BAD: 1からは2or3のみ。1->2->4のような複数での推移はvalidでないとする。 ASSERT_FALSE(transitions.IsTransitionValid(State::kState1, State::kState4)); // BAD: 3からの推移は登録されていない ASSERT_FALSE(transitions.IsTransitionValid(State::kState3, State::kState4)); ``` なおプロダクションコードでは以下のようなDCHECKを挟んであげるとstateのチェックができる。 ```cpp void DCheckStateTransition(State old_state, State new_state) { #if DCHECK_IS_ON()   static const base::NoDestructor<StateTransitions<State>> transitions( StateTransitions<State>({ {kState1, {kState2, kState3}}, {kState2, {kState3}}, {kState3, {}}, })); DCHECK_STATE_TRANSITION(transitions, old_state, new_state); #endif // DCHECK_IS_ON() } ``` 一応DCHECK時にしかチェックしないモデルなので、謎vectorのシングルトンが多少増える分には構わないということでNoDestructorで作っている。 ## 中身 Stateは好きに渡すことができるテンプレートとして定義されている。 ただし、State型は以下を満たす必要がある。 - operator== が定義されている - コピー可能 例えばenum classはこれを満たす。 ```cpp template <typename State> struct StateTransitions { ...; }; ``` やることは簡単。 State transitionは、出発地点の`State`と目的地の`vector<State>`のペアのvectorとして保存されている。 ```cpp struct StateTransition { StateTransition(State source, std::vector<State> destinations) : source(std::move(source)), destinations(std::move(destinations)) {} const State source; const std::vector<State> destinations; }; const std::vector<StateTransition> state_transitions; ``` そして、`source`と`destination`を受け取って、それがルールにマッチするかを確認する関数さえあれば良いのでそれを[`IsTransitionValid`](https://source.chromium.org/chromium/chromium/src/+/main:base/state_transitions.h;l=85;drc=e4622aaeccea84652488d1822c28c78b7115684f)として提供しており、これは`source`にマッチするStateTransitionをとってきてその`destination`の中にbase::Containしているかを確認するだけ。 std::vectorの中をfor文で探索するよりbase::small_mapでStateの数に応じてmapにしたりvectorにしてもらうほうが厳密には効率的だと思うが、Stateはそんなにないであろうことと、DCHECK専用のクラスであることから、ここではvectorで簡易的に実装されているんだと思う。