From 8e3ad413cf53e3e2f7009811da485f6961e2d4d9 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Mon, 14 Sep 2026 15:33:54 +0100 Subject: [PATCH] Initial working version --- include/etl/cyclic_value.h | 335 +++++++++++++++++++++++-------------- 1 file changed, 205 insertions(+), 130 deletions(-) diff --git a/include/etl/cyclic_value.h b/include/etl/cyclic_value.h index 964ed664..c85fc875 100644 --- a/include/etl/cyclic_value.h +++ b/include/etl/cyclic_value.h @@ -38,10 +38,60 @@ SOFTWARE. #include "platform.h" #include "algorithm.h" #include "exception.h" +#include "integral_limits.h" +#include "static_assert.h" #include "type_traits.h" namespace etl { + namespace private_cyclic_value + { + //************************************************************************* + /// Advances a value and wraps it at the supplied limits. + ///\param value The current value. + ///\param min_value The minimum value. + ///\param max_value The maximum value. + ///\param n The number of steps. + ///\return The advanced value. + //************************************************************************* + template + ETL_NODISCARD + static ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, TStep n) ETL_NOEXCEPT + { + typedef typename etl::make_unsigned::type unsigned_type; + + if (n == 0) + { + return value; + } + + // Check for full range limits. + const bool is_full_range = (min_value == etl::integral_limits::min) && (max_value == etl::integral_limits::max); + + if (is_full_range) + { + // This is safe as the type's own overflow behaviour is modulo wrap-around. + return static_cast(etl::to_unsigned(value) + static_cast(n)); + } + else + { + // Size of the wrap-around range. + const unsigned_type range = (etl::to_unsigned(max_value) - etl::to_unsigned(min_value)) + 1U; + + // Value expressed as an offset from min_value (0 .. range - 1). + const unsigned_type current = etl::to_unsigned(value) - etl::to_unsigned(min_value); + + // Reduce the step to something within a single lap of the range. + const unsigned_type step = etl::absolute_unsigned(n) % range; + + // Direction-corrected offset to add, always in [0, range). + const unsigned_type offset = (n > 0) ? step : (range - step) % range; + + return static_cast(etl::to_unsigned(min_value) + (current + offset) % range); + } + } + } // namespace private_cyclic_value + //*************************************************************************** /// Provides a value that cycles between two limits. //*************************************************************************** @@ -65,7 +115,7 @@ namespace etl /// Default constructor. /// The initial value is set to the first value. //************************************************************************* - ETL_CONSTEXPR cyclic_value() + ETL_CONSTEXPR cyclic_value() ETL_NOEXCEPT : value(First) { } @@ -75,15 +125,15 @@ namespace etl /// Set to an initial value. /// Clamped to the range. //************************************************************************* - ETL_CONSTEXPR14 explicit cyclic_value(T initial) + ETL_CONSTEXPR explicit cyclic_value(T initial) ETL_NOEXCEPT + : value(etl::clamp(initial, First, Last)) { - set(initial); } //************************************************************************* /// Copy constructor. //************************************************************************* - ETL_CONSTEXPR cyclic_value(const cyclic_value& other) + ETL_CONSTEXPR cyclic_value(const cyclic_value& other) ETL_NOEXCEPT : value(other.value) { } @@ -91,19 +141,45 @@ namespace etl //************************************************************************* /// Assignment operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) + ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) ETL_NOEXCEPT { value = other.value; return *this; } + //************************************************************************* + /// Adds a number of steps and clamps to the range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator+=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a number of steps and clamps to the range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator-=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_cyclic_value::advance(value, First, Last, -n); + return *this; + } + //************************************************************************* /// Sets the value. /// Truncates to the First/Last range. ///\param value The value. //************************************************************************* - ETL_CONSTEXPR14 void set(T value_) + ETL_CONSTEXPR14 void set(T value_) ETL_NOEXCEPT { value = etl::clamp(value_, First, Last); } @@ -111,7 +187,7 @@ namespace etl //************************************************************************* /// Resets the value to the first in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_first() + ETL_CONSTEXPR14 void to_first() ETL_NOEXCEPT { value = First; } @@ -119,7 +195,7 @@ namespace etl //************************************************************************* /// Resets the value to the last in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_last() + ETL_CONSTEXPR14 void to_last() ETL_NOEXCEPT { value = Last; } @@ -128,38 +204,17 @@ namespace etl /// Advances to value by a number of steps. ///\param n The number of steps to advance. //************************************************************************* - ETL_CONSTEXPR14 void advance(int n) + template + ETL_CONSTEXPR14 void advance(TStep n) ETL_NOEXCEPT { - if (n > 0) - { - for (int i = 0; i < n; ++i) - { - operator++(); - } - } - else - { - for (int i = 0; i < -n; ++i) - { - operator--(); - } - } - } - - //************************************************************************* - /// Conversion operator. - /// \return The value of the underlying type. - //************************************************************************* - ETL_CONSTEXPR14 operator T() - { - return value; + value = private_cyclic_value::advance(value, First, Last, n); } //************************************************************************* /// Const conversion operator. /// \return The value of the underlying type. //************************************************************************* - ETL_CONSTEXPR operator const T() const + ETL_CONSTEXPR operator T() const ETL_NOEXCEPT { return value; } @@ -167,16 +222,9 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator++() + ETL_CONSTEXPR14 cyclic_value& operator++() ETL_NOEXCEPT { - if (value >= Last) ETL_UNLIKELY - { - value = First; - } - else - { - ++value; - } + value = private_cyclic_value::advance(value, First, Last, 1); return *this; } @@ -184,7 +232,7 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator++(int) + ETL_CONSTEXPR14 cyclic_value operator++(int) ETL_NOEXCEPT { cyclic_value temp(*this); @@ -196,16 +244,9 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator--() + ETL_CONSTEXPR14 cyclic_value& operator--() ETL_NOEXCEPT { - if (value <= First) ETL_UNLIKELY - { - value = Last; - } - else - { - --value; - } + value = private_cyclic_value::advance(value, First, Last, int(-1)); return *this; } @@ -213,7 +254,7 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator--(int) + ETL_CONSTEXPR14 cyclic_value operator--(int) ETL_NOEXCEPT { cyclic_value temp(*this); @@ -225,7 +266,7 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(T t) + ETL_CONSTEXPR14 cyclic_value& operator=(T t) ETL_NOEXCEPT { set(t); return *this; @@ -234,8 +275,8 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - template - ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) + template + ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) ETL_NOEXCEPT { set(other.get()); return *this; @@ -244,7 +285,7 @@ namespace etl //************************************************************************* /// Gets the value. //************************************************************************* - ETL_CONSTEXPR T get() const + ETL_CONSTEXPR T get() const ETL_NOEXCEPT { return value; } @@ -252,7 +293,7 @@ namespace etl //************************************************************************* /// Gets the first value. //************************************************************************* - static ETL_CONSTEXPR T first() + static ETL_CONSTEXPR T first() ETL_NOEXCEPT { return First; } @@ -260,7 +301,7 @@ namespace etl //************************************************************************* /// Gets the last value. //************************************************************************* - static ETL_CONSTEXPR T last() + static ETL_CONSTEXPR T last() ETL_NOEXCEPT { return Last; } @@ -268,7 +309,7 @@ namespace etl //************************************************************************* /// Swaps the values. //************************************************************************* - void swap(cyclic_value& other) + ETL_CONSTEXPR14 void swap(cyclic_value& other) ETL_NOEXCEPT { using ETL_OR_STD::swap; // Allow ADL @@ -278,7 +319,7 @@ namespace etl //************************************************************************* /// Swaps the values. //************************************************************************* - friend void swap(cyclic_value& lhs, cyclic_value& rhs) + friend ETL_CONSTEXPR14 void swap(cyclic_value& lhs, cyclic_value& rhs) ETL_NOEXCEPT { lhs.swap(rhs); } @@ -286,7 +327,7 @@ namespace etl //************************************************************************* /// Operator ==. //************************************************************************* - friend ETL_CONSTEXPR bool operator==(const cyclic_value& lhs, const cyclic_value& rhs) + friend ETL_CONSTEXPR bool operator==(const cyclic_value& lhs, const cyclic_value& rhs) ETL_NOEXCEPT { return lhs.value == rhs.value; } @@ -294,7 +335,7 @@ namespace etl //************************************************************************* /// Operator !=. //************************************************************************* - friend ETL_CONSTEXPR bool operator!=(const cyclic_value& lhs, const cyclic_value& rhs) + friend ETL_CONSTEXPR bool operator!=(const cyclic_value& lhs, const cyclic_value& rhs) ETL_NOEXCEPT { return !(lhs == rhs); } @@ -322,7 +363,7 @@ namespace etl /// Sets 'first' and 'last' to the template parameter values. /// The initial value is set to the first value. //************************************************************************* - ETL_CONSTEXPR cyclic_value() + ETL_CONSTEXPR cyclic_value() ETL_NOEXCEPT : value(First) , first_value(First) , last_value(Last) @@ -335,7 +376,7 @@ namespace etl ///\param first The first value in the range. ///\param last The last value in the range. //************************************************************************* - ETL_CONSTEXPR cyclic_value(T first_, T last_) + ETL_CONSTEXPR cyclic_value(T first_, T last_) ETL_NOEXCEPT : value(first_) , first_value(first_) , last_value(last_) @@ -349,17 +390,17 @@ namespace etl ///\param first The first value in the range. ///\param last The last value in the range. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value(T first_, T last_, T initial) - : first_value(first_) + ETL_CONSTEXPR cyclic_value(T first_, T last_, T initial) ETL_NOEXCEPT + : value(etl::clamp(initial, first_, last_)) + , first_value(first_) , last_value(last_) { - set(initial); } //************************************************************************* /// Copy constructor. //************************************************************************* - ETL_CONSTEXPR cyclic_value(const cyclic_value& other) + ETL_CONSTEXPR cyclic_value(const cyclic_value& other) ETL_NOEXCEPT : value(other.value) , first_value(other.first_value) , last_value(other.last_value) @@ -372,7 +413,7 @@ namespace etl ///\param first The first value in the range. ///\param last The last value in the range. //************************************************************************* - ETL_CONSTEXPR14 void set(T first_, T last_) + ETL_CONSTEXPR14 void set(T first_, T last_) ETL_NOEXCEPT { first_value = first_; last_value = last_; @@ -383,7 +424,7 @@ namespace etl /// Sets the value. ///\param value The value. //************************************************************************* - ETL_CONSTEXPR14 void set(T value_) + ETL_CONSTEXPR14 void set(T value_) ETL_NOEXCEPT { value = etl::clamp(value_, first_value, last_value); } @@ -391,7 +432,7 @@ namespace etl //************************************************************************* /// Resets the value to the first in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_first() + ETL_CONSTEXPR14 void to_first() ETL_NOEXCEPT { value = first_value; } @@ -399,7 +440,7 @@ namespace etl //************************************************************************* /// Resets the value to the last in the range. //************************************************************************* - ETL_CONSTEXPR14 void to_last() + ETL_CONSTEXPR14 void to_last() ETL_NOEXCEPT { value = last_value; } @@ -408,38 +449,16 @@ namespace etl /// Advances to value by a number of steps. ///\param n The number of steps to advance. //************************************************************************* - ETL_CONSTEXPR14 void advance(int n) + ETL_CONSTEXPR14 void advance(int n) ETL_NOEXCEPT { - if (n > 0) - { - for (int i = 0; i < n; ++i) - { - operator++(); - } - } - else - { - for (int i = 0; i < -n; ++i) - { - operator--(); - } - } - } - - //************************************************************************* - /// Conversion operator. - /// \return The value of the underlying type. - //************************************************************************* - ETL_CONSTEXPR14 operator T() - { - return value; + value = private_cyclic_value::advance(value, first_value, last_value, n); } //************************************************************************* /// Const conversion operator. /// \return The value of the underlying type. //************************************************************************* - ETL_CONSTEXPR operator const T() const + ETL_CONSTEXPR operator T() const ETL_NOEXCEPT { return value; } @@ -447,16 +466,9 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator++() + ETL_CONSTEXPR14 cyclic_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value >= last_value) - { - value = first_value; - } - else - { - ++value; - } + value = private_cyclic_value::advance(value, first_value, last_value, 1); return *this; } @@ -464,7 +476,7 @@ namespace etl //************************************************************************* /// ++ operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator++(int) + ETL_CONSTEXPR14 cyclic_value operator++(int) ETL_NOEXCEPT { cyclic_value temp(*this); @@ -476,16 +488,9 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator--() + ETL_CONSTEXPR14 cyclic_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { - if (value <= first_value) - { - value = last_value; - } - else - { - --value; - } + value = private_cyclic_value::advance(value, first_value, last_value, int(-1)); return *this; } @@ -493,7 +498,7 @@ namespace etl //************************************************************************* /// -- operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value operator--(int) + ETL_CONSTEXPR14 cyclic_value operator--(int) ETL_NOEXCEPT { cyclic_value temp(*this); @@ -505,7 +510,7 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(T t) + ETL_CONSTEXPR14 cyclic_value& operator=(T t) ETL_NOEXCEPT { set(t); return *this; @@ -514,7 +519,7 @@ namespace etl //************************************************************************* /// = operator. //************************************************************************* - ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) + ETL_CONSTEXPR14 cyclic_value& operator=(const cyclic_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT { value = other.value; first_value = other.first_value; @@ -522,10 +527,36 @@ namespace etl return *this; } + //************************************************************************* + /// Adds a number of steps and clamps to the range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator+=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + advance(n); + return *this; + } + + //************************************************************************* + /// Subtracts a number of steps and clamps to the range. + /// Floating-point steps may be fractional. NaN is rejected. + ///\param n The number of steps. + ///\return A reference to this value. + //************************************************************************* + template + ETL_CONSTEXPR14 cyclic_value& operator-=(TStep n) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT + { + value = private_cyclic_value::advance(value, first_value, last_value, -n); + return *this; + } + //************************************************************************* /// Gets the value. //************************************************************************* - ETL_CONSTEXPR T get() const + ETL_CONSTEXPR T get() const ETL_NOEXCEPT { return value; } @@ -533,7 +564,7 @@ namespace etl //************************************************************************* /// Gets the first value. //************************************************************************* - ETL_CONSTEXPR T first() const + ETL_CONSTEXPR T first() const ETL_NOEXCEPT { return first_value; } @@ -541,7 +572,7 @@ namespace etl //************************************************************************* /// Gets the last value. //************************************************************************* - ETL_CONSTEXPR T last() const + ETL_CONSTEXPR T last() const ETL_NOEXCEPT { return last_value; } @@ -549,7 +580,7 @@ namespace etl //************************************************************************* /// Swaps the values. //************************************************************************* - void swap(cyclic_value& other) + ETL_CONSTEXPR14 void swap(cyclic_value& other) ETL_NOEXCEPT { using ETL_OR_STD::swap; // Allow ADL @@ -561,27 +592,71 @@ namespace etl //************************************************************************* /// Swaps the values. //************************************************************************* - friend void swap(cyclic_value& lhs, cyclic_value& rhs) + friend ETL_CONSTEXPR14 void swap(cyclic_value& lhs, cyclic_value& rhs) ETL_NOEXCEPT { lhs.swap(rhs); } //************************************************************************* - /// Operator ==. + /// Operator == //************************************************************************* - friend ETL_CONSTEXPR bool operator==(const cyclic_value& lhs, const cyclic_value& rhs) + friend ETL_CONSTEXPR bool operator==(const cyclic_value& lhs, const cyclic_value& rhs) ETL_NOEXCEPT { - return (lhs.value == rhs.value) && (lhs.first_value == rhs.first_value) && (lhs.last_value == rhs.last_value); + return (lhs.value == rhs.value); } //************************************************************************* - /// Operator !=. + /// Operator != //************************************************************************* - friend ETL_CONSTEXPR bool operator!=(const cyclic_value& lhs, const cyclic_value& rhs) + friend ETL_CONSTEXPR bool operator!=(const cyclic_value& lhs, const cyclic_value& rhs) ETL_NOEXCEPT { return !(lhs == rhs); } + //************************************************************************* + /// Operator < + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is less than rhs. + //************************************************************************* + friend ETL_CONSTEXPR bool operator<(const cyclic_value& lhs, const cyclic_value& rhs) ETL_NOEXCEPT + { + return lhs.value < rhs.value; + } + + //************************************************************************* + /// Operator <= + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is less than or equal to rhs. + //************************************************************************* + friend ETL_CONSTEXPR bool operator<=(const cyclic_value& lhs, const cyclic_value& rhs) ETL_NOEXCEPT + { + return !(rhs < lhs); + } + + //************************************************************************* + /// Operator > + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is greater than rhs. + //************************************************************************* + friend ETL_CONSTEXPR bool operator>(const cyclic_value& lhs, const cyclic_value& rhs) ETL_NOEXCEPT + { + return rhs < lhs; + } + + //************************************************************************* + /// Operator >= + ///\param lhs The left-hand operand. + ///\param rhs The right-hand operand. + ///\return `true` if lhs is greater than or equal to rhs. + //************************************************************************* + friend ETL_CONSTEXPR bool operator>=(const cyclic_value& lhs, const cyclic_value& rhs) ETL_NOEXCEPT + { + return !(lhs < rhs); + } + private: T value; ///< The current value.