1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef BASE_STATE_TRANSITIONS_H_
6 #define BASE_STATE_TRANSITIONS_H_
10 #include "base/check_op.h"
11 #include "base/containers/contains.h"
12 #include "base/no_destructor.h"
16 // This class represents a set of state transitions where each state is a value
17 // that supports copy, << and == (e.g. an enum element). It's intended to be
18 // used in DCHECK-enabled builds to check that only valid transitions occur. Its
19 // implementation favours convenience and simplicity over performance. To use it
20 // follow this example:
30 // // This may require exporting the symbol (e.g. CONTENT_EXPORT) if it will be
31 // // used by any other components: one common way this can happen is if the
32 // // enum is logged in tests (e.g. via gtest's EXPECT_* macros).
33 // std::ostream& operator<<(std::ostream& o, const State& s);
38 // #include "base/no_destructor.h"
39 // #include "base/state_transitions.h"
41 // std::ostream& operator<<(std::ostream& o, const State& s) {
42 // return o << static_cast<int>(s);
45 // void DCheckStateTransition(State old_state, State new_state) {
47 // static const base::NoDestructor<StateTransitions<State>> transitions(
48 // StateTransitions<State>({
49 // {kState1, {kState2, kState3}},
50 // {kState2, {kState3}},
53 // DCHECK_STATE_TRANSITION(transitions, old_state, new_state);
54 // #endif // DCHECK_IS_ON()
58 template <typename State>
59 struct StateTransitions {
61 // Represents a state and all of the states that are valid transitions from
63 struct StateTransition {
64 StateTransition(State source, std::vector<State> destinations)
65 : source(std::move(source)), destinations(std::move(destinations)) {}
68 const std::vector<State> destinations;
71 explicit StateTransitions(std::vector<StateTransition> state_transitions)
72 : state_transitions(std::move(state_transitions)) {}
74 // Returns a list of states that are valid to transition to from |source|.
75 const std::vector<State>& GetValidTransitions(const State& source) const {
76 for (const StateTransition& state_transition : state_transitions) {
77 if (state_transition.source == source)
78 return state_transition.destinations;
80 static const base::NoDestructor<std::vector<State>> no_transitions;
81 return *no_transitions;
84 // Tests whether transitioning from |source| to |destination| is valid.
85 bool IsTransitionValid(const State& source, const State& destination) const {
86 return base::Contains(GetValidTransitions(source), destination);
89 const std::vector<StateTransition> state_transitions;
92 // DCHECK if transitioning from |old_state| to |new_state| is not valid
93 // according to |transitions|.
94 #define DCHECK_STATE_TRANSITION(transitions, old_state, new_state) \
95 DCHECK((transitions)->IsTransitionValid((old_state), (new_state))) \
96 << "Invalid transition: " << old_state << " -> " << new_state
100 #endif // BASE_STATE_TRANSITIONS_H_