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 96 : UpdatableCRTP() __attribute__((no_sanitize("undefined"))) { 51 96 : updatables.append(CRTP(Derived)); 52 96 : } 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 96 : virtual ~UpdatableCRTP() __attribute__((no_sanitize("undefined"))) { 69 96 : if (updatables.couldContain(CRTP(Derived))) 70 80 : updatables.remove(CRTP(Derived)); 71 192 : } 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 38 : for (auto &el : updatables) 85 14 : (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 : 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 : static void beginAll() { Updatable::applyToAll(&Updatable::begin); } 183 : 184 : /// Update all enabled instances of this class 185 : /// @see update() 186 : static void updateAll() { Updatable::applyToAll(&Updatable::update); } 187 : 188 : /// @} 189 : }; 190 : 191 : END_AH_NAMESPACE