1*6777b538SAndroid Build Coastguard Worker // Copyright 2020 The Chromium Authors 2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be 3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file. 4*6777b538SAndroid Build Coastguard Worker 5*6777b538SAndroid Build Coastguard Worker #ifndef BASE_STATE_TRANSITIONS_H_ 6*6777b538SAndroid Build Coastguard Worker #define BASE_STATE_TRANSITIONS_H_ 7*6777b538SAndroid Build Coastguard Worker 8*6777b538SAndroid Build Coastguard Worker #include <vector> 9*6777b538SAndroid Build Coastguard Worker 10*6777b538SAndroid Build Coastguard Worker #include "base/check_op.h" 11*6777b538SAndroid Build Coastguard Worker #include "base/containers/contains.h" 12*6777b538SAndroid Build Coastguard Worker #include "base/no_destructor.h" 13*6777b538SAndroid Build Coastguard Worker 14*6777b538SAndroid Build Coastguard Worker namespace base { 15*6777b538SAndroid Build Coastguard Worker 16*6777b538SAndroid Build Coastguard Worker // This class represents a set of state transitions where each state is a value 17*6777b538SAndroid Build Coastguard Worker // that supports copy, << and == (e.g. an enum element). It's intended to be 18*6777b538SAndroid Build Coastguard Worker // used in DCHECK-enabled builds to check that only valid transitions occur. Its 19*6777b538SAndroid Build Coastguard Worker // implementation favours convenience and simplicity over performance. To use it 20*6777b538SAndroid Build Coastguard Worker // follow this example: 21*6777b538SAndroid Build Coastguard Worker 22*6777b538SAndroid Build Coastguard Worker // In foo.h 23*6777b538SAndroid Build Coastguard Worker // --------- 24*6777b538SAndroid Build Coastguard Worker // enum class State { 25*6777b538SAndroid Build Coastguard Worker // kState1, 26*6777b538SAndroid Build Coastguard Worker // kState2, 27*6777b538SAndroid Build Coastguard Worker // kState3, 28*6777b538SAndroid Build Coastguard Worker // }; 29*6777b538SAndroid Build Coastguard Worker // 30*6777b538SAndroid Build Coastguard Worker // // This may require exporting the symbol (e.g. CONTENT_EXPORT) if it will be 31*6777b538SAndroid Build Coastguard Worker // // used by any other components: one common way this can happen is if the 32*6777b538SAndroid Build Coastguard Worker // // enum is logged in tests (e.g. via gtest's EXPECT_* macros). 33*6777b538SAndroid Build Coastguard Worker // std::ostream& operator<<(std::ostream& o, const State& s); 34*6777b538SAndroid Build Coastguard Worker // --------- 35*6777b538SAndroid Build Coastguard Worker // 36*6777b538SAndroid Build Coastguard Worker // In foo.cc 37*6777b538SAndroid Build Coastguard Worker // --------- 38*6777b538SAndroid Build Coastguard Worker // #include "base/no_destructor.h" 39*6777b538SAndroid Build Coastguard Worker // #include "base/state_transitions.h" 40*6777b538SAndroid Build Coastguard Worker // 41*6777b538SAndroid Build Coastguard Worker // std::ostream& operator<<(std::ostream& o, const State& s) { 42*6777b538SAndroid Build Coastguard Worker // return o << static_cast<int>(s); 43*6777b538SAndroid Build Coastguard Worker // } 44*6777b538SAndroid Build Coastguard Worker // 45*6777b538SAndroid Build Coastguard Worker // void DCheckStateTransition(State old_state, State new_state) { 46*6777b538SAndroid Build Coastguard Worker // #if DCHECK_IS_ON() 47*6777b538SAndroid Build Coastguard Worker // static const base::NoDestructor<StateTransitions<State>> transitions( 48*6777b538SAndroid Build Coastguard Worker // StateTransitions<State>({ 49*6777b538SAndroid Build Coastguard Worker // {kState1, {kState2, kState3}}, 50*6777b538SAndroid Build Coastguard Worker // {kState2, {kState3}}, 51*6777b538SAndroid Build Coastguard Worker // {kState3, {}}, 52*6777b538SAndroid Build Coastguard Worker // })); 53*6777b538SAndroid Build Coastguard Worker // DCHECK_STATE_TRANSITION(transitions, old_state, new_state); 54*6777b538SAndroid Build Coastguard Worker // #endif // DCHECK_IS_ON() 55*6777b538SAndroid Build Coastguard Worker // } 56*6777b538SAndroid Build Coastguard Worker // --------- 57*6777b538SAndroid Build Coastguard Worker 58*6777b538SAndroid Build Coastguard Worker template <typename State> 59*6777b538SAndroid Build Coastguard Worker struct StateTransitions { 60*6777b538SAndroid Build Coastguard Worker public: 61*6777b538SAndroid Build Coastguard Worker // Represents a state and all of the states that are valid transitions from 62*6777b538SAndroid Build Coastguard Worker // it. 63*6777b538SAndroid Build Coastguard Worker struct StateTransition { StateTransitionStateTransitions::StateTransition64*6777b538SAndroid Build Coastguard Worker StateTransition(State source, std::vector<State> destinations) 65*6777b538SAndroid Build Coastguard Worker : source(std::move(source)), destinations(std::move(destinations)) {} 66*6777b538SAndroid Build Coastguard Worker 67*6777b538SAndroid Build Coastguard Worker const State source; 68*6777b538SAndroid Build Coastguard Worker const std::vector<State> destinations; 69*6777b538SAndroid Build Coastguard Worker }; 70*6777b538SAndroid Build Coastguard Worker StateTransitionsStateTransitions71*6777b538SAndroid Build Coastguard Worker explicit StateTransitions(std::vector<StateTransition> state_transitions) 72*6777b538SAndroid Build Coastguard Worker : state_transitions(std::move(state_transitions)) {} 73*6777b538SAndroid Build Coastguard Worker 74*6777b538SAndroid Build Coastguard Worker // Returns a list of states that are valid to transition to from |source|. GetValidTransitionsStateTransitions75*6777b538SAndroid Build Coastguard Worker const std::vector<State>& GetValidTransitions(const State& source) const { 76*6777b538SAndroid Build Coastguard Worker for (const StateTransition& state_transition : state_transitions) { 77*6777b538SAndroid Build Coastguard Worker if (state_transition.source == source) 78*6777b538SAndroid Build Coastguard Worker return state_transition.destinations; 79*6777b538SAndroid Build Coastguard Worker } 80*6777b538SAndroid Build Coastguard Worker static const base::NoDestructor<std::vector<State>> no_transitions; 81*6777b538SAndroid Build Coastguard Worker return *no_transitions; 82*6777b538SAndroid Build Coastguard Worker } 83*6777b538SAndroid Build Coastguard Worker 84*6777b538SAndroid Build Coastguard Worker // Tests whether transitioning from |source| to |destination| is valid. IsTransitionValidStateTransitions85*6777b538SAndroid Build Coastguard Worker bool IsTransitionValid(const State& source, const State& destination) const { 86*6777b538SAndroid Build Coastguard Worker return base::Contains(GetValidTransitions(source), destination); 87*6777b538SAndroid Build Coastguard Worker } 88*6777b538SAndroid Build Coastguard Worker 89*6777b538SAndroid Build Coastguard Worker const std::vector<StateTransition> state_transitions; 90*6777b538SAndroid Build Coastguard Worker }; 91*6777b538SAndroid Build Coastguard Worker 92*6777b538SAndroid Build Coastguard Worker // DCHECK if transitioning from |old_state| to |new_state| is not valid 93*6777b538SAndroid Build Coastguard Worker // according to |transitions|. 94*6777b538SAndroid Build Coastguard Worker #define DCHECK_STATE_TRANSITION(transitions, old_state, new_state) \ 95*6777b538SAndroid Build Coastguard Worker DCHECK((transitions)->IsTransitionValid((old_state), (new_state))) \ 96*6777b538SAndroid Build Coastguard Worker << "Invalid transition: " << old_state << " -> " << new_state 97*6777b538SAndroid Build Coastguard Worker 98*6777b538SAndroid Build Coastguard Worker } // namespace base 99*6777b538SAndroid Build Coastguard Worker 100*6777b538SAndroid Build Coastguard Worker #endif // BASE_STATE_TRANSITIONS_H_ 101