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