rusEFI
The most advanced open source ECU
type_list.h
Go to the documentation of this file.
1 #pragma once
2 
3 /*
4  * Indicates that a member of type_list should be able to be replaced in a unit test.
5  */
6 template<typename base_t>
7 struct Mockable;
8 
9 /*
10  * Instantiates each type, allowing you to fetch by type.
11  *
12  * This is basically std::tuple, std::get, and std::apply, but with the API oriented more toward
13  * our needs.
14  *
15  * The primary use of this is to provide a list of engine modules in the giant Engine structure.
16  *
17  * The main feature is that objects are mockable. If you pass in Mockable<T> instead of T, and
18  * typedef T::interface_t, then you can call set to change what get returns.
19  *
20  * For a normal type T,
21  * T & get<T>();
22  * T & unmock<T>();
23  *
24  * For Mockable<T>,
25  * T::interface_t & get<T>();
26  * T & unmock<T>();
27  */
28 template<typename base_t, typename... tail_t>
29 struct type_list {
31  type_list<tail_t...> others;
32 
33  static_assert(!decltype(others)::template has<base_t>(),
34  "Each type can only be listed once.");
35 
36  /*
37  * Call the given function on the unmocked version of each type.
38  *
39  * It is probably best (and maybe only possible?) to call this with a generic lambda, as:
40  * tl.apply_all([](auto & m) { m.WhateverFuncIWant(); });
41  */
42  template<typename func_t>
43  void apply_all(func_t const & f) {
44  first.apply_all(f);
45  others.apply_all(f);
46  }
47 
48  // Applies an accumulator function over the sequence of elements.
49  // The specified seed value is used as the initial accumulator value,
50  // and the specified function is used to select the result value.
51  template<typename return_t, typename func_t>
52  auto aggregate(func_t const& accumulator, return_t seed) {
53  return others.aggregate(accumulator, first.aggregate(accumulator, seed));
54  }
55 
56  /*
57  * Return the container object for type get_t.
58  *
59  * get_t should not be Mockable, it should be the actual type.
60  * The return object will have the methods unmock(), operator->, operator*, and if
61  * Mockable, set().
62  *
63  * The return type is type_list<get_t> or type_list<Mockable<get_t>>
64  */
65  template<typename get_t>
66  constexpr auto get() -> std::enable_if_t<decltype(first)::template has<get_t>(),
67  decltype(first.template get<get_t>())> {
68  return first.template get<get_t>();
69  }
70 
71  template<typename get_t>
72  constexpr auto get() -> std::enable_if_t<!decltype(first)::template has<get_t>(),
73  decltype(others.template get<get_t>())> {
74  return others.template get<get_t>();
75  }
76 
77  /*
78  * Returns whether has_t exists in the type list
79  *
80  * has_t should not be Mockable, it should be the actual type.
81  */
82  template<typename has_t>
83  static constexpr bool has() {
84  return decltype(first)::template has<has_t>() ||
85  decltype(others)::template has<has_t>();
86  }
87 };
88 
89 /*
90  * Specialization of type_list for a single base_t (that is not Mockable<>).
91  */
92 template<typename base_t>
94 private:
95  base_t me;
96 
97 public:
98  template<typename func_t>
99  void apply_all(func_t const & f) {
100  f(me);
101  }
102 
103  template<typename return_t, typename func_t>
104  auto aggregate(func_t const& accumulator, return_t seed) {
105  return accumulator(me, seed);
106  }
107 
108  template<typename has_t>
109  static constexpr bool has() {
110  return std::is_same_v<has_t, base_t>;
111  }
112 
113  template<typename get_t, typename = std::enable_if_t<has<get_t>()>>
114  constexpr auto & get() {
115  return *this;
116  }
117 
118  constexpr auto & unmock() {
119  return me;
120  }
121 
122  constexpr base_t * operator->() {
123  return &me;
124  }
125 
126  constexpr base_t & operator*() {
127  return me;
128  }
129 };
130 
131 #if EFI_PROD_CODE
132 
133 /*
134  * Production specialization of type_list for a single Mockable<base_t>.
135  *
136  * Performs exactly as base_t.
137  */
138 template<typename base_t>
139 struct type_list<Mockable<base_t>> : public type_list<base_t> {
140 };
141 
142 #else // if not EFI_PROD_CODE:
143 
144 #include <memory>
145 
146 /*
147  * Unit test/simulator specialization of type_list for a single Mockable<base_t>.
148  */
149 template<typename base_t>
150 struct type_list<Mockable<base_t>> {
151 private:
152  // Dynamically allocate so that ASAN can detect overflows for us
153  std::unique_ptr<base_t> me = std::make_unique<base_t>();
154  typename base_t::interface_t * cur = me.get();
155 
156 public:
157  template<typename func_t>
158  void apply_all(func_t const & f) {
159  f(*me);
160  }
161 
162  template<typename return_t, typename func_t>
163  auto aggregate(func_t const& accumulator, return_t seed) {
164  return accumulator(*me, seed);
165  }
166 
167  template<typename has_t>
168  static constexpr bool has() {
169  return std::is_same_v<has_t, base_t>;
170  }
171 
172  template<typename get_t, typename = std::enable_if_t<has<get_t>()>>
173  constexpr auto & get() {
174  return *this;
175  }
176 
177  auto & unmock() {
178  return *me;
179  }
180 
181  void set(typename base_t::interface_t * ptr) {
182  if (ptr) {
183  cur = ptr;
184  } else {
185  cur = me.get();
186  }
187  }
188 
189  constexpr auto * operator->() {
190  return cur;
191  }
192 
193  constexpr auto & operator*() {
194  return *cur;
195  }
196 
197 };
198 
199 #endif // EFI_UNIT_TEST
void set(typename base_t::interface_t *ptr)
Definition: type_list.h:181
constexpr auto & operator*()
Definition: type_list.h:193
constexpr auto & get()
Definition: type_list.h:173
void apply_all(func_t const &f)
Definition: type_list.h:158
constexpr auto * operator->()
Definition: type_list.h:189
static constexpr bool has()
Definition: type_list.h:168
auto aggregate(func_t const &accumulator, return_t seed)
Definition: type_list.h:163
constexpr base_t * operator->()
Definition: type_list.h:122
auto aggregate(func_t const &accumulator, return_t seed)
Definition: type_list.h:104
constexpr base_t & operator*()
Definition: type_list.h:126
constexpr auto & unmock()
Definition: type_list.h:118
constexpr auto & get()
Definition: type_list.h:114
static constexpr bool has()
Definition: type_list.h:109
void apply_all(func_t const &f)
Definition: type_list.h:99
static constexpr bool has()
Definition: type_list.h:83
auto aggregate(func_t const &accumulator, return_t seed)
Definition: type_list.h:52
constexpr auto get() -> std::enable_if_t< decltype(first)::template has< get_t >(), decltype(first.template get< get_t >())>
Definition: type_list.h:66
void apply_all(func_t const &f)
Definition: type_list.h:43
type_list< base_t > first
Definition: type_list.h:30
constexpr auto get() -> std::enable_if_t<!decltype(first)::template has< get_t >(), decltype(others.template get< get_t >())>
Definition: type_list.h:72
type_list< tail_t... > others
Definition: type_list.h:31