Line | Branch | Decision | Exec | Source |
---|---|---|---|---|
1 | /** | |||
2 | * @author Matthew Kennedy, (c) 2019 | |||
3 | * | |||
4 | * This lets us compose multiple functions in to a single function. If we have | |||
5 | * conversion functions F(x), G(x), and H(x), we can define a new function | |||
6 | * FuncChain<F, G, H> that will compute H(G(F(X))). F first, then G, then H. | |||
7 | */ | |||
8 | ||||
9 | #pragma once | |||
10 | ||||
11 | #include "sensor_converter_func.h" | |||
12 | ||||
13 | #include <type_traits> | |||
14 | #include <utility> | |||
15 | ||||
16 | namespace priv { | |||
17 | template <class... _Types> | |||
18 | class FuncChain; | |||
19 | ||||
20 | template <> | |||
21 | class FuncChain<> { | |||
22 | protected: | |||
23 | 9 | SensorResult convert(float input) const { | ||
24 | // Base case is the identity function | |||
25 | 9 | return input; | ||
26 | } | |||
27 | ||||
28 | ✗ | void showInfo(float testInputValue) const { | ||
29 | // base case does nothing | |||
30 | (void)testInputValue; | |||
31 | ✗ | } | ||
32 | }; | |||
33 | ||||
34 | template <typename TFirst, typename... TRest> | |||
35 | class FuncChain<TFirst, TRest...> : private FuncChain<TRest...> { | |||
36 | static_assert(std::is_base_of_v<SensorConverter, TFirst>, "Template parameters must inherit from SensorConverter"); | |||
37 | ||||
38 | private: | |||
39 | using TBase = FuncChain<TRest...>; | |||
40 | ||||
41 | public: | |||
42 | 20 | SensorResult convert(float input) const { | ||
43 | // Convert the current step | |||
44 | 20 | SensorResult currentStep = m_f.convert(input); | ||
45 | ||||
46 | // if it was valid, pass this result to the chain of (n-1) functions that remain | |||
47 |
9/22priv::FuncChain<SubOne>::convert(float) const:
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
priv::FuncChain<Doubler, SubOne>::convert(float) const:
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
priv::FuncChain<Doubler>::convert(float) const:
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
priv::FuncChain<AddOne, Doubler, SubOne>::convert(float) const:
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
priv::FuncChain<AddOne, Doubler>::convert(float) const:
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
priv::FuncChain<AddOne>::convert(float) const:
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
priv::FuncChain<MafFilter>::convert(float) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
priv::FuncChain<MafTable, MafFilter>::convert(float) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
priv::FuncChain<MafVoltageCheck, MafTable, MafFilter>::convert(float) const:
✗ Branch 0 not taken.
✗ Branch 1 not taken.
priv::FuncChain<ThermistorFunc>::convert(float) const:
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
priv::FuncChain<ResistanceFunc, ThermistorFunc>::convert(float) const:
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
|
20 | if (currentStep.Valid) { | |
48 | 18 | return TBase::convert(currentStep.Value); | ||
49 | } else { | |||
50 | 2 | return SensorResult(currentStep.Code); | ||
51 | } | |||
52 | } | |||
53 | ||||
54 | // Get the element in the current level | |||
55 | template <class TGet> | |||
56 | 14 | std::enable_if_t<std::is_same_v<TGet, TFirst>, TGet &> get() { | ||
57 | 14 | return m_f; | ||
58 | } | |||
59 | ||||
60 | template <class TGet> | |||
61 | 5 | std::enable_if_t<std::is_same_v<TGet, TFirst>, TGet *> getPtr() { | ||
62 | 5 | return &m_f; | ||
63 | } | |||
64 | ||||
65 | // We don't have it - check level (n - 1) | |||
66 | template <class TGet> | |||
67 | 10 | std::enable_if_t<!std::is_same_v<TGet, TFirst>, TGet &> get() { | ||
68 | 10 | return TBase::template get<TGet>(); | ||
69 | } | |||
70 | ||||
71 | // We don't have it - check level (n - 1) | |||
72 | template <class TGet> | |||
73 | 5 | std::enable_if_t<!std::is_same_v<TGet, TFirst>, TGet *> getPtr() { | ||
74 | 5 | return TBase::template getPtr<TGet>(); | ||
75 | } | |||
76 | ||||
77 | ✗ | void showInfo(float testInputValue) const { | ||
78 | // Print info about this level | |||
79 | ✗ | m_f.showInfo(testInputValue); | ||
80 | ||||
81 | // If valid, recurse down | |||
82 | ✗ | auto res = m_f.convert(testInputValue); | ||
83 | ✗ | if (res.Valid) { | ||
84 | ✗ | TBase::showInfo(res.Value); | ||
85 | } | |||
86 | ✗ | } | ||
87 | ||||
88 | private: | |||
89 | TFirst m_f; | |||
90 | }; | |||
91 | } // namespace priv | |||
92 | ||||
93 | template <typename... TFuncs> | |||
94 | class FuncChain : public SensorConverter { | |||
95 | public: | |||
96 | // Perform chained conversion of all functions in TFuncs | |||
97 | 11 | SensorResult convert(float input) const override { | ||
98 | 11 | return m_fs.convert(input); | ||
99 | } | |||
100 | ||||
101 | // Access the sub-function of type TGet | |||
102 | template <typename TGet> | |||
103 | 14 | TGet &get() { | ||
104 | 14 | return m_fs.template get<TGet>(); | ||
105 | } | |||
106 | ||||
107 | // references would be sometimes implicitly deleted, right? adding parallel pointer API which would stay? | |||
108 | template <typename TGet> | |||
109 | 5 | TGet *getPtr() { | ||
110 | 5 | return m_fs.template getPtr<TGet>(); | ||
111 | } | |||
112 | ||||
113 | ✗ | void showInfo(float testInputValue) const override { | ||
114 | ✗ | m_fs.showInfo(testInputValue); | ||
115 | ✗ | } | ||
116 | ||||
117 | private: | |||
118 | priv::FuncChain<TFuncs...> m_fs; | |||
119 | }; | |||
120 |