Line data Source code
1 : /* ✔ */
2 :
3 : #pragma once
4 :
5 : #include <AH/Containers/CRTP.hpp>
6 : #include <AH/Containers/LinkedList.hpp>
7 : #include <AH/Error/Error.hpp>
8 : #include <AH/STL/type_traits>
9 : #include <AH/STL/utility> // std::forward
10 : #include <AH/Settings/SettingsWrapper.hpp>
11 : #include <stddef.h>
12 :
13 : BEGIN_AH_NAMESPACE
14 :
15 : /**
16 : * @brief A super class for object that have to be updated regularly.
17 : *
18 : * All instances of this class are kept in a linked list, so it's easy to
19 : * iterate over all of them to update them.
20 : *
21 : * This version uses static polymorphism using the Curiously Recurring Template
22 : * Pattern. This requires less virtual function calls.
23 : * (Only the destructor is virtual.)
24 : *
25 : * @nosubgrouping
26 : */
27 : template <class Derived>
28 : class UpdatableCRTP : public DoublyLinkable<Derived> {
29 :
30 : public:
31 : #if defined(__GNUC__) && !defined(__clang__)
32 : #pragma GCC diagnostic push
33 : #pragma GCC diagnostic ignored "-Wattributes"
34 : #endif
35 :
36 : // When a Derived instance is constructed, the base class constructor
37 : // UpdatableCRTP is called first. Because the Derived constructor hasn't
38 : // been run yet, the dynamic type is just Updatable, not yet Derived.
39 : // The undefined behavior sanitizer checks this dynamic type when the this
40 : // pointer is casted to Derived using the CRTP macro, and thus causes an
41 : // error.
42 : // The constructor only casts and stores the pointer, it doesn't dereference
43 : // it to call any of Derived methods, so I don't think that it's undefined
44 : // behavior (and I don't know of a better way to do this).
45 : // Also see https://stackoverflow.com/q/61061051/6356744
46 :
47 : protected:
48 : /// Constructor: create an Updatable and add it to the linked list of
49 : /// instances.
50 395 : UpdatableCRTP() __attribute__((no_sanitize("undefined"))) {
51 395 : updatables.append(CRTP(Derived));
52 395 : }
53 :
54 : UpdatableCRTP(const UpdatableCRTP &)
55 : __attribute__((no_sanitize("undefined")))
56 : : DoublyLinkable<Derived>() {
57 : updatables.append(CRTP(Derived));
58 : }
59 : UpdatableCRTP &operator=(const UpdatableCRTP &) { return *this; }
60 :
61 : UpdatableCRTP(UpdatableCRTP &&) __attribute__((no_sanitize("undefined"))) {
62 : updatables.append(CRTP(Derived));
63 : }
64 : UpdatableCRTP &operator=(UpdatableCRTP &&) { return *this; }
65 :
66 : public:
67 : /// Destructor: remove the updatable from the linked list of instances.
68 395 : virtual ~UpdatableCRTP() __attribute__((no_sanitize("undefined"))) {
69 395 : if (updatables.couldContain(CRTP(Derived)))
70 379 : updatables.remove(CRTP(Derived));
71 790 : }
72 :
73 : #if defined(__GNUC__) && !defined(__clang__)
74 : #pragma GCC diagnostic pop
75 : #endif
76 :
77 : public:
78 : /// @name Main initialization and updating methods
79 : /// @{
80 :
81 : template <class... Args>
82 : static void __attribute__((always_inline))
83 : applyToAll(void (Derived::*method)(Args...), Args... args) {
84 112 : for (auto &el : updatables)
85 57 : (el.*method)(args...);
86 : }
87 :
88 : /// @}
89 :
90 : public:
91 : /// @name Enabling and disabling updatables
92 : /// @{
93 :
94 : /// Enable this updatable: insert it into the linked list of instances,
95 : /// so it gets updated automatically
96 17 : void enable() {
97 17 : if (isEnabled()) {
98 1 : ERROR(F("Error: This element is already enabled."), 0x1212);
99 : return; // LCOV_EXCL_LINE
100 : }
101 16 : updatables.append(CRTP(Derived));
102 : }
103 :
104 : /// Disable this updatable: remove it from the linked list of instances,
105 : /// so it no longer gets updated automatically
106 33 : void disable() {
107 33 : if (!isEnabled()) {
108 1 : ERROR(F("Error: This element is already disabled."), 0x1213);
109 : return; // LCOV_EXCL_LINE
110 : }
111 32 : updatables.remove(CRTP(Derived));
112 : }
113 :
114 : /**
115 : * @brief Check if this updatable is enabled.
116 : *
117 : * @note Assumes that the updatable is not added to a different linked
118 : * list by the user.
119 : */
120 98 : bool isEnabled() const {
121 98 : return updatables.couldContain(CRTP(const Derived));
122 : }
123 :
124 : /// @copydoc enable()
125 : static void enable(UpdatableCRTP *element) { element->enable(); }
126 : /// @copydoc enable()
127 16 : static void enable(UpdatableCRTP &element) { element.enable(); }
128 : /// @copydoc enable()
129 : template <class U, size_t N>
130 1 : static void enable(U (&array)[N]) {
131 17 : for (U &el : array)
132 16 : enable(el);
133 1 : }
134 :
135 : /// @copydoc disable()
136 : static void disable(UpdatableCRTP *element) { element->disable(); }
137 : /// @copydoc disable()
138 32 : static void disable(UpdatableCRTP &element) { element.disable(); }
139 : /// @copydoc disable()
140 : template <class U, size_t N>
141 2 : static void disable(U (&array)[N]) {
142 34 : for (U &el : array)
143 32 : disable(el);
144 2 : }
145 :
146 : /// Move down this element in the list.
147 67 : void moveDown() { updatables.moveDown(CRTP(Derived)); }
148 :
149 : /// @}
150 :
151 : protected:
152 : static DoublyLinkedList<Derived> updatables;
153 : };
154 :
155 : template <class Derived>
156 : DoublyLinkedList<Derived> UpdatableCRTP<Derived>::updatables;
157 :
158 : struct NormalUpdatable {};
159 :
160 : /**
161 : * @brief A super class for object that have to be updated regularly.
162 : *
163 : * All instances of this class are kept in a linked list, so it's easy to
164 : * iterate over all of them to update them.
165 : *
166 : * @nosubgrouping
167 : */
168 : template <class T = NormalUpdatable>
169 : class Updatable : public UpdatableCRTP<Updatable<T>> {
170 : public:
171 : /// @name Main initialization and updating methods
172 : /// @{
173 :
174 : /// Initialize this updatable.
175 : virtual void begin() = 0;
176 :
177 : /// Update this updatable.
178 : virtual void update() = 0;
179 :
180 : /// Begin all enabled instances of this class
181 : /// @see begin()
182 7 : static void beginAll() { Updatable::applyToAll(&Updatable::begin); }
183 :
184 : /// Update all enabled instances of this class
185 : /// @see update()
186 26 : static void updateAll() { Updatable::applyToAll(&Updatable::update); }
187 :
188 : /// @}
189 : };
190 :
191 : END_AH_NAMESPACE
|