state_transitions.h 3.3 KB

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