| // Copyright 2020 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #ifndef BASE_STATE_TRANSITIONS_H_ | 
 | #define BASE_STATE_TRANSITIONS_H_ | 
 |  | 
 | #include <vector> | 
 |  | 
 | #include "base/check_op.h" | 
 | #include "base/containers/contains.h" | 
 | #include "base/no_destructor.h" | 
 |  | 
 | namespace base { | 
 |  | 
 | // This class represents a set of state transitions where each state is a value | 
 | // that supports copy, << and == (e.g. an enum element). It's intended to be | 
 | // used in DCHECK-enabled builds to check that only valid transitions occur. Its | 
 | // implementation favours convenience and simplicity over performance. To use it | 
 | // follow this example: | 
 |  | 
 | // In foo.h | 
 | // --------- | 
 | // enum class State { | 
 | //   kState1, | 
 | //   kState2, | 
 | //   kState3, | 
 | // }; | 
 | // | 
 | // // This may require exporting the symbol (e.g. CONTENT_EXPORT) if it will be | 
 | // // used by any other components: one common way this can happen is if the | 
 | // // enum is logged in tests (e.g. via gtest's EXPECT_* macros). | 
 | // std::ostream& operator<<(std::ostream& o, const State& s); | 
 | // --------- | 
 | // | 
 | // In foo.cc | 
 | // --------- | 
 | // #include "base/no_destructor.h" | 
 | // #include "base/state_transitions.h" | 
 | // | 
 | // std::ostream& operator<<(std::ostream& o, const State& s) { | 
 | //   return o << static_cast<int>(s); | 
 | // } | 
 | // | 
 | // 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() | 
 | // } | 
 | // --------- | 
 |  | 
 | template <typename State> | 
 | struct StateTransitions { | 
 |  public: | 
 |   // Represents a state and all of the states that are valid transitions from | 
 |   // it. | 
 |   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; | 
 |   }; | 
 |  | 
 |   explicit StateTransitions(std::vector<StateTransition> state_transitions) | 
 |       : state_transitions(std::move(state_transitions)) {} | 
 |  | 
 |   // Returns a list of states that are valid to transition to from |source|. | 
 |   const std::vector<State>& GetValidTransitions(const State& source) const { | 
 |     for (const StateTransition& state_transition : state_transitions) { | 
 |       if (state_transition.source == source) | 
 |         return state_transition.destinations; | 
 |     } | 
 |     static const base::NoDestructor<std::vector<State>> no_transitions; | 
 |     return *no_transitions; | 
 |   } | 
 |  | 
 |   // Tests whether transitioning from |source| to |destination| is valid. | 
 |   bool IsTransitionValid(const State& source, const State& destination) const { | 
 |     return base::Contains(GetValidTransitions(source), destination); | 
 |   } | 
 |  | 
 |   const std::vector<StateTransition> state_transitions; | 
 | }; | 
 |  | 
 | // DCHECK if transitioning from |old_state| to |new_state| is not valid | 
 | // according to |transitions|. | 
 | #define DCHECK_STATE_TRANSITION(transitions, old_state, new_state)   \ | 
 |   DCHECK((transitions)->IsTransitionValid((old_state), (new_state))) \ | 
 |       << "Invalid transition: " << old_state << " -> " << new_state | 
 |  | 
 | }  // namespace base | 
 |  | 
 | #endif  // BASE_STATE_TRANSITIONS_H_ |