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