LCOV - code coverage report
Current view: top level - src/AH/Containers - Updatable.hpp (source / functions) Coverage Total Hit
Test: 73449d9b107c772cf65493691543348214e5d5eb Lines: 100.0 % 32 32
Test Date: 2026-06-06 17:44:35 Functions: 83.8 % 37 31
Legend: Lines:     hit not hit

            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
        

Generated by: LCOV version 2.4-beta