GCC Code Coverage Report


Directory: ./
File: firmware/util/containers/type_list.h
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 95.7% 2611 0 2729
Functions: 94.7% 996 0 1052
Branches: 50.0% 6 0 12
Decisions: 50.0% 5 - 10

Line Branch Decision Exec Source
1 #pragma once
2
3 #include <type_traits>
4
5 /*
6 * Indicates that a member of type_list should be able to be replaced in a unit test.
7 */
8 template<typename base_t>
9 struct Mockable;
10
11 template<typename... Ts>
12 struct type_list;
13
14 /*
15 * Instantiates each type, allowing you to fetch by type.
16 *
17 * This is basically std::tuple, std::get, and std::apply, but with the API oriented more toward
18 * our needs.
19 *
20 * The primary use of this is to provide a list of engine modules in the giant Engine structure.
21 *
22 * The main feature is that objects are mockable. If you pass in Mockable<T> instead of T, and
23 * typedef T::interface_t, then you can call set to change what get returns.
24 *
25 * For a normal type T,
26 * T & get<T>();
27 * T & unmock<T>();
28 *
29 * For Mockable<T>,
30 * T::interface_t & get<T>();
31 * T & unmock<T>();
32 */
33 template<typename base_t, typename... tail_t>
34 struct type_list<base_t, tail_t...> {
35 type_list<base_t> first;
36 type_list<tail_t...> others;
37
38 #if EFI_UNIT_TEST
39 20310 virtual ~type_list() = default;
40 #endif
41
42 static_assert(!decltype(others)::template has<base_t>(), "Each type can only be listed once.");
43
44 template<typename count_t>
45 static consteval size_t count() {
46 return decltype(first)::template count<count_t>() +
47 decltype(others)::template count<count_t>();
48 }
49
50 static consteval size_t count() {
51 return 1 + decltype(others)::count();
52 }
53
54 /*
55 * Returns whether has_t exists in the type list
56 *
57 * has_t should not be Mockable, it should be the actual type.
58 */
59 template<typename has_t>
60 static consteval bool has() {
61 return decltype(first)::template has<has_t>() ||
62 decltype(others)::template has<has_t>();
63 }
64
65 /*
66 * Call the given function on the unmocked version of each type.
67 *
68 * It is probably best (and maybe only possible?) to call this with a generic lambda, as:
69 * tl.apply_all([](auto & m) { m.WhateverFuncIWant(); });
70 */
71 template<typename func_t>
72 1029750 void constexpr apply_all(func_t const & f) {
73 1029750 first.apply_all(f);
74 1029750 others.apply_all(f);
75 1029750 }
76
77 template<typename func_t>
78 void constexpr apply_all(func_t const & f) const {
79 first.apply_all(f);
80 others.apply_all(f);
81 }
82
83 // Applies an accumulator function over the sequence of elements.
84 // The specified seed value is used as the initial accumulator value,
85 // and the specified function is used to select the result value.
86 template<typename return_t, typename func_t>
87 30030 decltype(auto) aggregate(func_t const& accumulator, return_t seed) {
88 30030 return others.aggregate(accumulator, first.aggregate(accumulator, seed));
89 }
90
91 template<typename return_t, typename func_t>
92 decltype(auto) aggregate(func_t const& accumulator, return_t seed) const {
93 return others.aggregate(accumulator, first.aggregate(accumulator, seed));
94 }
95
96 /*
97 * Return the container object for type get_t.
98 *
99 * get_t should not be Mockable, it should be the actual type.
100 * The return object will have the methods unmock(), operator->, operator*, and if
101 * Mockable, set().
102 *
103 * The return type is type_list<get_t> or type_list<Mockable<get_t>>
104 */
105 template<typename get_t>
106 97840059 constexpr decltype(auto) get() {
107 static_assert(has<get_t>(), "Type not found in type_list");
108 if constexpr (decltype(first)::template has<get_t>()) {
109 4422556 return first.template get<get_t>();
110 } else {
111 93417503 return others.template get<get_t>();
112 }
113 }
114
115 template<typename get_t>
116 constexpr decltype(auto) get() const {
117 static_assert(has<get_t>(), "Type not found in type_list");
118 if constexpr (decltype(first)::template has<get_t>()) {
119 return first.template get<get_t>();
120 } else {
121 return others.template get<get_t>();
122 }
123 }
124 };
125
126 /*
127 * Specialization of type_list for a single base_t (that is not Mockable<>).
128 */
129 template<typename base_t>
130 struct type_list<base_t> {
131 private:
132 base_t me;
133
134 public:
135 template<typename count_t>
136 static consteval size_t count() {
137 return std::is_same_v<base_t, count_t> ? 1 : 0;
138 }
139
140 static consteval size_t count() {
141 return 1;
142 }
143
144 template<typename func_t>
145 755150 void constexpr apply_all(func_t const & f) {
146 755150 f(me);
147 755150 }
148
149 template<typename func_t>
150 void constexpr apply_all(func_t const & f) const {
151 f(me);
152 }
153
154 template<typename return_t, typename func_t>
155 22022 decltype(auto) aggregate(func_t const& accumulator, return_t seed) {
156 22022 return accumulator(me, seed);
157 }
158
159 template<typename return_t, typename func_t>
160 decltype(auto) aggregate(func_t const& accumulator, return_t seed) const {
161 return accumulator(me, seed);
162 }
163
164 template<typename has_t>
165 static consteval bool has() {
166 return std::is_same_v<has_t, base_t>;
167 }
168
169 template<typename get_t, typename = std::enable_if_t<has<get_t>()>>
170 4257623 constexpr auto& get() {
171 4257623 return *this;
172 }
173
174 template<typename get_t, typename = std::enable_if_t<has<get_t>()>>
175 constexpr auto const& get() const {
176 return *this;
177 }
178
179 4288 constexpr auto& unmock() {
180 4288 return me;
181 }
182
183 constexpr auto const& unmock() const {
184 return me;
185 }
186
187 3729178 constexpr auto operator->() {
188 3729178 return &me;
189 }
190
191 constexpr auto operator->() const {
192 return &me;
193 }
194
195 1 constexpr auto& operator*() {
196 1 return me;
197 }
198
199 constexpr auto const& operator*() const {
200 return me;
201 }
202 };
203
204 #if EFI_PROD_CODE
205
206 /*
207 * Production specialization of type_list for a single Mockable<base_t>.
208 *
209 * Performs exactly as base_t.
210 */
211 template<typename base_t>
212 struct type_list<Mockable<base_t>> : type_list<base_t> {
213 };
214
215 #else // if not EFI_PROD_CODE:
216
217 #include <memory>
218
219 /*
220 * Unit test/simulator specialization of type_list for a single Mockable<base_t>.
221 */
222 template<typename base_t>
223 struct type_list<Mockable<base_t>> {
224 private:
225 // Dynamically allocate so that ASAN can detect overflows for us
226 std::unique_ptr<base_t> me = std::make_unique<base_t>();
227 using interface_t = typename base_t::interface_t;
228 interface_t* cur = me.get();
229
230 public:
231 template<typename count_t>
232 static consteval size_t count() {
233 return std::is_same_v<base_t, count_t> ? 1 : 0;
234 }
235
236 static consteval size_t count() {
237 return 1;
238 }
239
240 template<typename func_t>
241 308925 void constexpr apply_all(func_t const & f) {
242 308925 f(*me);
243 308925 }
244
245 template<typename func_t>
246 void constexpr apply_all(func_t const & f) const {
247 f(*me);
248 }
249
250 template<typename return_t, typename func_t>
251 decltype(auto) aggregate(func_t const& accumulator, return_t seed) {
252 return accumulator(*me, seed);
253 }
254
255 template<typename return_t, typename func_t>
256 decltype(auto) aggregate(func_t const& accumulator, return_t seed) const {
257 return accumulator(*me, seed);
258 }
259
260 template<typename has_t>
261 static consteval bool has() {
262 return std::is_same_v<has_t, base_t>;
263 }
264
265 template<typename get_t, typename = std::enable_if_t<has<get_t>()>>
266 164933 constexpr auto& get() {
267 164933 return *this;
268 }
269
270 template<typename get_t, typename = std::enable_if_t<has<get_t>()>>
271 constexpr auto const& get() const {
272 return *this;
273 }
274
275 auto& unmock() {
276 return *me;
277 }
278
279 auto const& unmock() const {
280 return *me;
281 }
282
283 22 void constexpr set(interface_t* ptr) {
284
6/12
type_list<Mockable<IdleController> >::set(IdleController*):
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
type_list<Mockable<WallFuelController> >::set(IWallFuelController*):
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
type_list<Mockable<InjectorModelPrimary> >::set(IInjectorModel*):
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
type_list<Mockable<AcController> >::set(AcController*):
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
type_list<Mockable<InjectorModelSecondary> >::set(IInjectorModel*):
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
type_list<Mockable<IgnitionController> >::set(IgnitionController*):
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
5/10
type_list<Mockable<IdleController> >::set(IdleController*):
✓ Decision 'true' taken 6 times.
✗ Decision 'false' not taken.
type_list<Mockable<WallFuelController> >::set(IWallFuelController*):
✓ Decision 'true' taken 1 time.
✗ Decision 'false' not taken.
type_list<Mockable<InjectorModelPrimary> >::set(IInjectorModel*):
✓ Decision 'true' taken 12 times.
✗ Decision 'false' not taken.
type_list<Mockable<AcController> >::set(AcController*):
✓ Decision 'true' taken 1 time.
✗ Decision 'false' not taken.
type_list<Mockable<IgnitionController> >::set(IgnitionController*):
✓ Decision 'true' taken 1 time.
✗ Decision 'false' not taken.
22 if (ptr) {
285 22 cur = ptr;
286 } else {
287 cur = me.get();
288 }
289 22 }
290
291 13430 constexpr interface_t* operator->() {
292 13430 return cur;
293 }
294
295 constexpr interface_t const* operator->() const {
296 return cur;
297 }
298
299 1 constexpr interface_t& operator*() {
300 1 return *cur;
301 }
302
303 constexpr interface_t const& operator*() const {
304 return *cur;
305 }
306 };
307
308 #endif // EFI_UNIT_TEST
309