etl/include/etl/clamped_value.h
Drew Rife d9a9488582
Add referenced clamped values with shared ranges (#1597)
* Support runtime floating-point clamped_value before C++20

* Add referenced clamped values with shared ranges
2026-09-26 10:45:48 +01:00

1379 lines
52 KiB
C++

///\file
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2026 John Wellbelove
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#ifndef ETL_CLAMPED_VALUE_INCLUDED
#define ETL_CLAMPED_VALUE_INCLUDED
#include "platform.h"
#include "absolute.h"
#include "algorithm.h"
#include "error_handler.h"
#include "functional.h"
#include "limits.h"
#include "math.h"
#include "static_assert.h"
#include "type_traits.h"
#include "utility.h"
///\def ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
/// Set to `1` when clamped_value supports floating-point types.
/// Runtime floating-point support requires C++11 variadic templates.
/// The macro may be defined by the build or platform profile to override
/// automatic detection.
#if !defined(ETL_HAS_FLOATING_POINT_CLAMPED_VALUE)
#if ETL_USING_CPP11
#define ETL_HAS_FLOATING_POINT_CLAMPED_VALUE 1
#else
#define ETL_HAS_FLOATING_POINT_CLAMPED_VALUE 0
#endif
#endif
///\def ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE
/// Set to `1` when clamped_value supports floating-point compile-time bounds.
#if !defined(ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE)
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE && ETL_USING_CPP20 && defined(__cpp_nontype_template_args) && (__cpp_nontype_template_args >= 201911L)
#define ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE 1
#else
#define ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE 0
#endif
#endif
///\defgroup clamped_value clamped_value
/// Provides a value that is clamped between two limits.
/// Integral types are supported in all language modes. Floating-point types
/// with runtime bounds are supported when
/// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`.
/// \ingroup utilities
namespace etl
{
/// \cond INTERNAL
namespace private_clamped_value
{
//*************************************************************************
/// Type policy for clamped_value.
/// Integral values use a signed difference type and unsigned saturation
/// arithmetic. Floating-point values use `T` as the difference type.
//*************************************************************************
template <typename T, bool IsIntegral = etl::is_integral<T>::value>
struct traits;
template <typename T>
struct traits<T, true>
{
typedef typename etl::make_signed<T>::type difference_type;
typedef typename etl::make_unsigned<T>::type unsigned_type;
typedef etl::numeric_limits<T> limits_type;
typedef etl::numeric_limits<difference_type> difference_limits_type;
};
template <typename T>
struct traits<T, false>
{
ETL_STATIC_ASSERT(ETL_HAS_FLOATING_POINT_CLAMPED_VALUE&& etl::is_floating_point<T>::value,
"clamped_value requires an integral type, or a floating-point type when supported");
typedef T difference_type;
typedef etl::numeric_limits<T> limits_type;
typedef etl::numeric_limits<T> difference_limits_type;
};
template <typename T, bool IsIntegral = etl::is_integral<T>::value>
struct arithmetic;
//*************************************************************************
/// Saturating arithmetic policy for integral values.
//*************************************************************************
template <typename T>
struct arithmetic<T, true>
{
typedef typename traits<T>::unsigned_type unsigned_type;
ETL_NODISCARD
static
ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, typename traits<T>::difference_type n) ETL_NOEXCEPT
{
if (n == 0)
{
return value;
}
const unsigned_type current = etl::to_unsigned(value); // static_cast<unsigned_type>(value);
const unsigned_type step = etl::absolute_unsigned(n);
if ((n > 0) && (value < max_value))
{
const unsigned_type distance = etl::to_unsigned(max_value) - current; // static_cast<unsigned_type>(max_value) - current;
value = (step >= distance) ? max_value : static_cast<T>(current + step);
}
else if ((n < 0) && (value > min_value))
{
const unsigned_type distance = current - etl::to_unsigned(min_value); // static_cast<unsigned_type>(min_value);
value = (step >= distance) ? min_value : static_cast<T>(current - step);
}
return value;
}
ETL_NODISCARD
static ETL_CONSTEXPR14 T subtract(T value, T min_value, T max_value, typename traits<T>::difference_type n) ETL_NOEXCEPT
{
if (n == traits<T>::difference_limits_type::lowest()) ETL_UNLIKELY
{
value = advance(value, min_value, max_value, traits<T>::difference_limits_type::max());
return advance(value, min_value, max_value, 1);
}
return advance(value, min_value, max_value, static_cast<typename traits<T>::difference_type>(-n));
}
};
#if ETL_HAS_FLOATING_POINT_CLAMPED_VALUE
//*************************************************************************
/// Saturating arithmetic policy for floating-point values.
//*************************************************************************
template <typename T>
struct arithmetic<T, false>
{
ETL_NODISCARD
static ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, T n) ETL_NOEXCEPT
{
if ((n > T(0)) && (value < max_value))
{
const T distance = max_value - value;
value = (n >= distance) ? max_value : static_cast<T>(value + n);
}
else if ((n < T(0)) && (value > min_value))
{
const T distance = value - min_value;
value = (-n >= distance) ? min_value : static_cast<T>(value + n);
}
return value;
}
ETL_NODISCARD
static ETL_CONSTEXPR14 T subtract(T value, T min_value, T max_value, T n) ETL_NOEXCEPT
{
return advance(value, min_value, max_value, -n);
}
};
#endif
//*************************************************************************
/// Advances a value and saturates it at the supplied bounds.
///\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 <typename T>
ETL_NODISCARD ETL_CONSTEXPR14 T advance(T value, T min_value, T max_value, typename traits<T>::difference_type n) ETL_NOEXCEPT
{
return arithmetic<T>::advance(value, min_value, max_value, n);
}
//*************************************************************************
/// Subtracts a value and saturates it at the supplied bounds.
/// Handles the lowest integral difference without signed overflow.
///\param value The current value.
///\param min_value The minimum value.
///\param max_value The maximum value.
///\param n The number of steps to subtract.
///\return The value after subtraction.
//*************************************************************************
template <typename T>
ETL_NODISCARD ETL_CONSTEXPR14 T subtract(T value, T min_value, T max_value, typename traits<T>::difference_type n) ETL_NOEXCEPT
{
return arithmetic<T>::subtract(value, min_value, max_value, n);
}
//*************************************************************************
/// Verifies that a clamped value, bound, or difference is not NaN.
/// Integral values always pass validation.
///\param value The value to validate.
//*************************************************************************
template <typename T>
ETL_CONSTEXPR14 void validate(T value)
{
ETL_ASSERT(!etl::is_nan(value), ETL_ERROR_GENERIC("clamped_value: NaN is not supported"));
}
//*************************************************************************
/// Validates a value and its bounds before clamping.
///\param value The value to clamp.
///\param min_value The minimum permitted value.
///\param max_value The maximum permitted value.
///\return The value clamped to the supplied range.
//*************************************************************************
template <typename T>
ETL_NODISCARD ETL_CONSTEXPR14 T validated_clamp(T value, T min_value, T max_value)
{
validate(value);
validate(min_value);
validate(max_value);
ETL_ASSERT(min_value <= max_value, ETL_ERROR_GENERIC("clamped_value: invalid range"));
return etl::clamp(value, min_value, max_value);
}
} // namespace private_clamped_value
/// \endcond
#if ETL_USING_CPP11
template <typename T, T... Limits>
class clamped_value;
#else // ETL_NOT_USING_CPP11
#include "private/diagnostic_float_equal_push.h"
template <typename T, T Min = T(), T Max = T(), bool RuntimeSpecialisation = ((Min == T()) && (Max == T()))>
class clamped_value;
#include "private/diagnostic_pop.h"
#endif
//***************************************************************************
/// Provides a value that is clamped between two compile-time limits.
/// Supports incrementing, decrementing and arbitrary advance.
///@tparam T An integral type, or a floating-point type when
/// ETL_HAS_COMPILE_TIME_FLOATING_POINT_CLAMPED_VALUE is `1`.
///@tparam Min The minimum value of the range.
///@tparam Max The maximum value of the range.
///\ingroup clamped_value
//***************************************************************************
#if ETL_USING_CPP11
template <typename T, T Min, T Max>
class clamped_value<T, Min, Max>
#else // ETL_NOT_USING_CPP11
template <typename T, T Min, T Max>
class clamped_value<T, Min, Max, false>
#endif
{
public:
/// The type used to advance or subtract from the stored value.
typedef typename private_clamped_value::traits<T>::difference_type difference_type;
/// Numeric limits for the stored value type.
typedef typename private_clamped_value::traits<T>::limits_type limits_type;
/// Numeric limits for difference_type.
typedef typename private_clamped_value::traits<T>::difference_limits_type difference_limits_type;
/// \cond INTERNAL
ETL_STATIC_ASSERT(Min <= Max, "clamped_value minimum must not exceed maximum");
/// \endcond
//*************************************************************************
/// Default constructor.
/// The initial value is set to the minimum value.
//*************************************************************************
ETL_CONSTEXPR clamped_value() ETL_NOEXCEPT
: value(Min)
{
}
//*************************************************************************
/// Constructor.
/// Set to an initial value.
/// Clamped to the range.
/// NaN is rejected for floating-point values.
///\param initial The initial value.
//*************************************************************************
ETL_CONSTEXPR14 explicit clamped_value(T initial)
: value(private_clamped_value::validated_clamp(initial, Min, Max))
{
}
//*************************************************************************
/// Copy constructor.
//*************************************************************************
ETL_CONSTEXPR clamped_value(const clamped_value& other) ETL_NOEXCEPT
: value(other.value)
{
}
//*************************************************************************
/// Assignment operator.
///\param other The value to copy.
///\return A reference to this value.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value& operator=(const clamped_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
{
if (this == &other)
{
return *this;
}
set(other.value);
return *this;
}
//*************************************************************************
/// Sets the value.
/// Clamps to the Min/Max range.
/// NaN is rejected for floating-point values.
///\param value_ The value.
//*************************************************************************
ETL_CONSTEXPR14 void set(T value_)
{
value = private_clamped_value::validated_clamp(value_, Min, Max);
}
//*************************************************************************
/// Resets the value to the minimum in the range.
//*************************************************************************
ETL_CONSTEXPR14 void to_min() ETL_NOEXCEPT
{
value = Min;
}
//*************************************************************************
/// Resets the value to the maximum in the range.
//*************************************************************************
ETL_CONSTEXPR14 void to_max() ETL_NOEXCEPT
{
value = Max;
}
//*************************************************************************
/// Advances the value by a number of steps.
/// Saturates at the Min/Max range.
/// Floating-point steps may be fractional. NaN is rejected.
///\param n The number of steps.
//*************************************************************************
ETL_CONSTEXPR14 void advance(difference_type n)
{
private_clamped_value::validate(n);
value = private_clamped_value::advance(value, Min, Max, n);
}
//*************************************************************************
/// Conversion operator.
/// \return The value of the underlying type.
//*************************************************************************
ETL_NODISCARD ETL_CONSTEXPR14 operator T() const ETL_NOEXCEPT
{
return value;
}
//*************************************************************************
/// ++ operator.
/// Advances by one.
/// Saturates at Max.
///\return A reference to this value.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
{
value = private_clamped_value::advance(value, Min, Max, difference_type(1));
return *this;
}
//*************************************************************************
/// ++ operator.
/// Saturates at Max.
///\return The value before incrementing.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value operator++(int) ETL_NOEXCEPT
{
clamped_value temp(*this);
++(*this);
return temp;
}
//*************************************************************************
/// -- operator.
/// Subtracts one.
/// Saturates at Min.
///\return A reference to this value.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
{
value = private_clamped_value::advance(value, Min, Max, difference_type(-1));
return *this;
}
//*************************************************************************
/// -- operator.
/// Saturates at Min.
///\return The value before decrementing.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value operator--(int) ETL_NOEXCEPT
{
clamped_value temp(*this);
--(*this);
return temp;
}
//*************************************************************************
/// = operator.
/// Clamps to the Min/Max range.
///\param value_ The value to assign.
///\return A reference to this value.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER
{
set(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.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER
{
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.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER
{
private_clamped_value::validate(n);
value = private_clamped_value::subtract(value, Min, Max, n);
return *this;
}
//*************************************************************************
/// Gets the value.
///\return The current value.
//*************************************************************************
ETL_NODISCARD ETL_CONSTEXPR T get() const ETL_NOEXCEPT
{
return value;
}
//*************************************************************************
/// Gets the minimum value.
///\return The minimum value.
//*************************************************************************
ETL_NODISCARD ETL_CONSTEXPR T min() const ETL_NOEXCEPT
{
return Min;
}
//*************************************************************************
/// Gets the maximum value.
///\return The maximum value.
//*************************************************************************
ETL_NODISCARD ETL_CONSTEXPR T max() const ETL_NOEXCEPT
{
return Max;
}
//*************************************************************************
/// Swaps the values.
///\param other The value to swap with.
//*************************************************************************
ETL_CONSTEXPR14 void swap(clamped_value& other) ETL_NOEXCEPT
{
using ETL_OR_STD::swap;
swap(value, other.value);
}
//*************************************************************************
/// Swaps the values.
///\param lhs The first value.
///\param rhs The second value.
//*************************************************************************
friend ETL_CONSTEXPR14 void swap(clamped_value& lhs, clamped_value& rhs) ETL_NOEXCEPT
{
lhs.swap(rhs);
}
//*************************************************************************
/// Operator ==.
///\param lhs The left-hand value.
///\param rhs The right-hand value.
///\return `true` if the values are equal.
//*************************************************************************
friend ETL_CONSTEXPR bool operator==(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT
{
#include "private/diagnostic_float_equal_push.h"
return lhs.value == rhs.value;
#include "private/diagnostic_pop.h"
}
//*************************************************************************
/// Operator !=.
///\param lhs The left-hand value.
///\param rhs The right-hand value.
///\return `true` if the values are not equal.
//*************************************************************************
friend ETL_CONSTEXPR bool operator!=(const clamped_value& lhs, const clamped_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 clamped_value& lhs, const clamped_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 clamped_value& lhs, const clamped_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 clamped_value& lhs, const clamped_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 clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT
{
return !(lhs < rhs);
}
/// @}
private:
T value;
};
//***************************************************************************
/// Provides a value that is clamped between two runtime limits.
/// Supports incrementing, decrementing and arbitrary advance.
///@tparam T An integral type, or a floating-point type when
/// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`.
///@tparam Min Ignored for this specialisation.
///@tparam Max Ignored for this specialisation.
///\ingroup clamped_value
//***************************************************************************
#if ETL_USING_CPP11
template <typename T>
class clamped_value<T>
#else // ETL_NOT_USING_CPP11
template <typename T, T Min, T Max>
class clamped_value<T, Min, Max, true>
#endif
{
public:
/// The type used to advance or subtract from the stored value.
typedef typename private_clamped_value::traits<T>::difference_type difference_type;
/// Numeric limits for the stored value type.
typedef typename private_clamped_value::traits<T>::limits_type limits_type;
/// Numeric limits for difference_type.
typedef typename private_clamped_value::traits<T>::difference_limits_type difference_limits_type;
//*************************************************************************
/// Default constructor.
/// Sets the value to the minimum representable value of `T` and the
/// bounds to the full range of `T`.
//*************************************************************************
ETL_CONSTEXPR clamped_value() ETL_NOEXCEPT
: value(limits_type::lowest())
, min_value(limits_type::lowest())
, max_value(limits_type::max())
{
}
//*************************************************************************
/// Constructor.
/// Sets the value to the minimum of the range.
/// Reversed bounds and NaN bounds are rejected.
///\param min_ The minimum value.
///\param max_ The maximum value.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value(T min_, T max_)
: value(min_)
, min_value(min_)
, max_value(max_)
{
private_clamped_value::validate(min_);
private_clamped_value::validate(max_);
ETL_ASSERT(min_ <= max_, ETL_ERROR_GENERIC("clamped_value: invalid range"));
}
//*************************************************************************
/// Constructor.
/// Set to an initial value.
/// Clamped to the range.
/// Reversed bounds and NaN values or bounds are rejected.
///\param min_ The minimum value.
///\param max_ The maximum value.
///\param initial The initial value.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value(T min_, T max_, T initial)
: value(private_clamped_value::validated_clamp(initial, min_, max_))
, min_value(min_)
, max_value(max_)
{
}
//*************************************************************************
/// Copy constructor.
///\param other The value to copy.
//*************************************************************************
ETL_CONSTEXPR clamped_value(const clamped_value& other) ETL_NOEXCEPT
: value(other.value)
, min_value(other.min_value)
, max_value(other.max_value)
{
}
//*************************************************************************
/// Sets the range.
/// Sets the value to the minimum of the range.
/// Reversed bounds and NaN bounds are rejected.
///\param min_ The minimum value.
///\param max_ The maximum value.
//*************************************************************************
ETL_CONSTEXPR14 void set(T min_, T max_)
{
private_clamped_value::validate(min_);
private_clamped_value::validate(max_);
ETL_ASSERT(min_ <= max_, ETL_ERROR_GENERIC("clamped_value: invalid range"));
min_value = min_;
max_value = max_;
value = min_;
}
//*************************************************************************
/// Sets the value.
/// Clamps to the runtime Min/Max range.
/// NaN is rejected for floating-point values.
///\param value_ The value.
//*************************************************************************
ETL_CONSTEXPR14 void set(T value_)
{
value = private_clamped_value::validated_clamp(value_, min_value, max_value);
}
//*************************************************************************
/// Resets the value to the minimum in the range.
//*************************************************************************
ETL_CONSTEXPR14 void to_min() ETL_NOEXCEPT
{
value = min_value;
}
//*************************************************************************
/// Resets the value to the maximum in the range.
//*************************************************************************
ETL_CONSTEXPR14 void to_max() ETL_NOEXCEPT
{
value = max_value;
}
//*************************************************************************
/// Advances the value by a number of steps.
/// Saturates at the runtime Min/Max range.
/// Floating-point steps may be fractional. NaN is rejected.
///\param n The number of steps.
//*************************************************************************
ETL_CONSTEXPR14 void advance(difference_type n)
{
private_clamped_value::validate(n);
value = private_clamped_value::advance(value, min_value, max_value, n);
}
//*************************************************************************
/// Conversion operator.
/// \return The value of the underlying type.
//*************************************************************************
ETL_NODISCARD ETL_CONSTEXPR14 operator T() const ETL_NOEXCEPT
{
return value;
}
//*************************************************************************
/// ++ operator.
/// Advances by one.
/// Saturates at the maximum.
///\return A reference to this value.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
{
value = private_clamped_value::advance(value, min_value, max_value, difference_type(1));
return *this;
}
//*************************************************************************
/// ++ operator.
/// Saturates at the maximum.
///\return The value before incrementing.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value operator++(int) ETL_NOEXCEPT
{
clamped_value temp(*this);
++(*this);
return temp;
}
//*************************************************************************
/// -- operator.
/// Subtracts one.
/// Saturates at the minimum.
///\return A reference to this value.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
{
value = private_clamped_value::advance(value, min_value, max_value, difference_type(-1));
return *this;
}
//*************************************************************************
/// -- operator.
/// Saturates at the minimum.
///\return The value before decrementing.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value operator--(int) ETL_NOEXCEPT
{
clamped_value temp(*this);
--(*this);
return temp;
}
//*************************************************************************
/// = operator.
/// Clamps to the runtime Min/Max range.
///\param value_ The value to assign.
///\return A reference to this value.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER
{
set(value_);
return *this;
}
//*************************************************************************
/// = operator.
/// Copies the value and runtime bounds.
///\param other The value to copy.
///\return A reference to this value.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value& operator=(const clamped_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
{
if (this == &other)
{
return *this;
}
value = other.value;
min_value = other.min_value;
max_value = other.max_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.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER
{
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.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER
{
private_clamped_value::validate(n);
value = private_clamped_value::subtract(value, min_value, max_value, n);
return *this;
}
//*************************************************************************
/// Gets the value.
///\return The current value.
//*************************************************************************
ETL_NODISCARD ETL_CONSTEXPR T get() const ETL_NOEXCEPT
{
return value;
}
//*************************************************************************
/// Gets the minimum value.
///\return The minimum value.
//*************************************************************************
ETL_NODISCARD ETL_CONSTEXPR T min() const ETL_NOEXCEPT
{
return min_value;
}
//*************************************************************************
/// Gets the maximum value.
///\return The maximum value.
//*************************************************************************
ETL_NODISCARD ETL_CONSTEXPR T max() const ETL_NOEXCEPT
{
return max_value;
}
//*************************************************************************
/// Swaps the values and runtime bounds.
///\param other The value to swap with.
//*************************************************************************
ETL_CONSTEXPR14 void swap(clamped_value& other) ETL_NOEXCEPT
{
using ETL_OR_STD::swap;
swap(value, other.value);
swap(min_value, other.min_value);
swap(max_value, other.max_value);
}
//*************************************************************************
/// Swaps the values and runtime bounds.
///\param lhs The first value.
///\param rhs The second value.
//*************************************************************************
friend ETL_CONSTEXPR14 void swap(clamped_value& lhs, clamped_value& rhs) ETL_NOEXCEPT
{
lhs.swap(rhs);
}
//*************************************************************************
/// Operator ==.
///\param lhs The left-hand value.
///\param rhs The right-hand value.
///\return `true` if the values are equal.
//*************************************************************************
friend ETL_CONSTEXPR bool operator==(const clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT
{
#include "private/diagnostic_float_equal_push.h"
return lhs.value == rhs.value;
#include "private/diagnostic_pop.h"
}
//*************************************************************************
/// Operator !=.
///\param lhs The left-hand value.
///\param rhs The right-hand value.
///\return `true` if the values differ.
//*************************************************************************
friend ETL_CONSTEXPR bool operator!=(const clamped_value& lhs, const clamped_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 clamped_value& lhs, const clamped_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 clamped_value& lhs, const clamped_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 clamped_value& lhs, const clamped_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 clamped_value& lhs, const clamped_value& rhs) ETL_NOEXCEPT
{
return !(lhs < rhs);
}
/// @}
private:
T value;
T min_value;
T max_value;
};
#if ETL_USING_CPP11
//***************************************************************************
/// An immutable pair of runtime limits that may be shared by multiple
/// referenced_clamped_value objects.
///@tparam T An integral type, or a floating-point type when
/// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`.
///\ingroup clamped_value
//***************************************************************************
template <typename T>
class clamped_value_range
{
public:
typedef typename private_clamped_value::traits<T>::limits_type limits_type;
//*************************************************************************
/// Constructs the full representable range of T.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value_range()
: min_value(limits_type::lowest())
, max_value(limits_type::max())
{
}
//*************************************************************************
/// Constructs a range with the supplied limits.
/// Reversed bounds and NaN bounds are rejected.
///\param min_ The minimum value.
///\param max_ The maximum value.
//*************************************************************************
ETL_CONSTEXPR14 clamped_value_range(T min_, T max_)
: min_value(min_)
, max_value(max_)
{
private_clamped_value::validate(min_);
private_clamped_value::validate(max_);
ETL_ASSERT(min_ <= max_, ETL_ERROR_GENERIC("clamped_value_range: invalid range"));
}
//*************************************************************************
/// Gets the minimum value.
//*************************************************************************
ETL_NODISCARD ETL_CONSTEXPR T min() const ETL_NOEXCEPT
{
return min_value;
}
//*************************************************************************
/// Gets the maximum value.
//*************************************************************************
ETL_NODISCARD ETL_CONSTEXPR T max() const ETL_NOEXCEPT
{
return max_value;
}
private:
const T min_value;
const T max_value;
};
//***************************************************************************
/// Provides a value clamped to a shared immutable runtime range.
/// The referenced range must outlive this object.
///@tparam T An integral type, or a floating-point type when
/// ETL_HAS_FLOATING_POINT_CLAMPED_VALUE is `1`.
///\ingroup clamped_value
//***************************************************************************
template <typename T>
class referenced_clamped_value
{
public:
/// The referenced range type.
typedef clamped_value_range<T> range_type;
/// The type used to advance or subtract from the stored value.
typedef typename private_clamped_value::traits<T>::difference_type difference_type;
/// Numeric limits for the stored value type.
typedef typename private_clamped_value::traits<T>::limits_type limits_type;
/// Numeric limits for difference_type.
typedef typename private_clamped_value::traits<T>::difference_limits_type difference_limits_type;
//*************************************************************************
/// Constructs a value initialized to the minimum of the referenced range.
///\param range_ The range to reference. It must outlive this object.
//*************************************************************************
explicit referenced_clamped_value(const range_type& range_)
: value(range_.min())
, value_range(etl::cref(range_))
{
}
//*************************************************************************
/// Constructs a value clamped to the referenced range.
///\param range_ The range to reference. It must outlive this object.
///\param initial The initial value.
//*************************************************************************
referenced_clamped_value(const range_type& range_, T initial)
: value(private_clamped_value::validated_clamp(initial, range_.min(), range_.max()))
, value_range(etl::cref(range_))
{
}
//*************************************************************************
/// Prevents construction with a temporary range.
//*************************************************************************
referenced_clamped_value(range_type&&) = delete;
referenced_clamped_value(range_type&&, T) = delete;
//*************************************************************************
/// Copies the value and its range reference.
///\param other The value to copy.
//*************************************************************************
referenced_clamped_value(const referenced_clamped_value& other) ETL_NOEXCEPT
: value(other.value)
, value_range(other.value_range)
{
}
//*************************************************************************
/// Copy assignment. Copies the value and its range reference.
///\param other The value and range reference to copy.
///\return A reference to this value.
//*************************************************************************
referenced_clamped_value& operator=(const referenced_clamped_value& other) ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
{
if (this != &other)
{
value = other.value;
value_range = other.value_range;
}
return *this;
}
//*************************************************************************
/// Sets the value and clamps it to the referenced range.
///\param value_ The value to assign.
//*************************************************************************
void set(T value_)
{
value = private_clamped_value::validated_clamp(value_, min(), max());
}
//*************************************************************************
/// Resets the value to the minimum of the referenced range.
//*************************************************************************
void to_min() ETL_NOEXCEPT
{
value = min();
}
//*************************************************************************
/// Resets the value to the maximum of the referenced range.
//*************************************************************************
void to_max() ETL_NOEXCEPT
{
value = max();
}
//*************************************************************************
/// Advances the value and saturates it at the referenced bounds.
///\param n The difference to add.
//*************************************************************************
void advance(difference_type n)
{
private_clamped_value::validate(n);
value = private_clamped_value::advance(value, min(), max(), n);
}
//*************************************************************************
/// Converts to the underlying value type.
///\return The current value.
//*************************************************************************
ETL_NODISCARD operator T() const ETL_NOEXCEPT
{
return value;
}
//*************************************************************************
/// Advances by one and saturates at the maximum.
///\return A reference to this value.
//*************************************************************************
referenced_clamped_value& operator++() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
{
value = private_clamped_value::advance(value, min(), max(), difference_type(1));
return *this;
}
//*************************************************************************
/// Advances by one and returns the previous value.
///\return The value before incrementing.
//*************************************************************************
referenced_clamped_value operator++(int) ETL_NOEXCEPT
{
referenced_clamped_value temp(*this);
++(*this);
return temp;
}
//*************************************************************************
/// Subtracts one and saturates at the minimum.
///\return A reference to this value.
//*************************************************************************
referenced_clamped_value& operator--() ETL_LVALUE_REF_QUALIFIER ETL_NOEXCEPT
{
value = private_clamped_value::advance(value, min(), max(), difference_type(-1));
return *this;
}
//*************************************************************************
/// Subtracts one and returns the previous value.
///\return The value before decrementing.
//*************************************************************************
referenced_clamped_value operator--(int) ETL_NOEXCEPT
{
referenced_clamped_value temp(*this);
--(*this);
return temp;
}
//*************************************************************************
/// Assigns a value and clamps it to the referenced range.
///\param value_ The value to assign.
///\return A reference to this value.
//*************************************************************************
referenced_clamped_value& operator=(T value_) ETL_LVALUE_REF_QUALIFIER
{
set(value_);
return *this;
}
//*************************************************************************
/// Adds a difference and clamps to the referenced range.
///\param n The difference to add.
///\return A reference to this value.
//*************************************************************************
referenced_clamped_value& operator+=(difference_type n) ETL_LVALUE_REF_QUALIFIER
{
advance(n);
return *this;
}
//*************************************************************************
/// Subtracts a difference and clamps to the referenced range.
///\param n The difference to subtract.
///\return A reference to this value.
//*************************************************************************
referenced_clamped_value& operator-=(difference_type n) ETL_LVALUE_REF_QUALIFIER
{
private_clamped_value::validate(n);
value = private_clamped_value::subtract(value, min(), max(), n);
return *this;
}
//*************************************************************************
/// Gets the current value.
///\return The current value.
//*************************************************************************
ETL_NODISCARD
T get() const ETL_NOEXCEPT
{
return value;
}
//*************************************************************************
/// Gets the minimum of the referenced range.
///\return The minimum value.
//*************************************************************************
ETL_NODISCARD
T min() const ETL_NOEXCEPT
{
return value_range.get().min();
}
//*************************************************************************
/// Gets the maximum of the referenced range.
///\return The maximum value.
//*************************************************************************
ETL_NODISCARD
T max() const ETL_NOEXCEPT
{
return value_range.get().max();
}
//*************************************************************************
/// Gets the referenced range.
///\return The referenced range.
//*************************************************************************
ETL_NODISCARD
const range_type& range() const ETL_NOEXCEPT
{
return value_range.get();
}
//*************************************************************************
/// Swaps the values and range references.
///\param other The value to swap with.
//*************************************************************************
void swap(referenced_clamped_value& other) ETL_NOEXCEPT
{
using ETL_OR_STD::swap;
swap(value, other.value);
swap(value_range, other.value_range);
}
//*************************************************************************
/// Swaps the values and range references.
///\param lhs The first value.
///\param rhs The second value.
//*************************************************************************
friend void swap(referenced_clamped_value& lhs, referenced_clamped_value& rhs) ETL_NOEXCEPT
{
lhs.swap(rhs);
}
//*************************************************************************
/// Compares current values for equality.
///\param lhs The left-hand value.
///\param rhs The right-hand value.
///\return `true` if the current values are equal.
//*************************************************************************
friend bool operator==(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
{
#include "private/diagnostic_float_equal_push.h"
return lhs.value == rhs.value;
#include "private/diagnostic_pop.h"
}
//*************************************************************************
/// Compares current values for inequality.
///\param lhs The left-hand value.
///\param rhs The right-hand value.
///\return `true` if the current values differ.
//*************************************************************************
friend bool operator!=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
{
return !(lhs == rhs);
}
//*************************************************************************
/// Compares current values using less-than.
///\param lhs The left-hand value.
///\param rhs The right-hand value.
///\return `true` if lhs is less than rhs.
//*************************************************************************
friend bool operator<(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
{
return lhs.value < rhs.value;
}
//*************************************************************************
/// Compares current values using less-than-or-equal.
///\param lhs The left-hand value.
///\param rhs The right-hand value.
///\return `true` if lhs is less than or equal to rhs.
//*************************************************************************
friend bool operator<=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
{
return !(rhs < lhs);
}
//*************************************************************************
/// Compares current values using greater-than.
///\param lhs The left-hand value.
///\param rhs The right-hand value.
///\return `true` if lhs is greater than rhs.
//*************************************************************************
friend bool operator>(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
{
return rhs < lhs;
}
//*************************************************************************
/// Compares current values using greater-than-or-equal.
///\param lhs The left-hand value.
///\param rhs The right-hand value.
///\return `true` if lhs is greater than or equal to rhs.
//*************************************************************************
friend bool operator>=(const referenced_clamped_value& lhs, const referenced_clamped_value& rhs) ETL_NOEXCEPT
{
return !(lhs < rhs);
}
private:
T value;
etl::reference_wrapper<const range_type> value_range;
};
#endif
} // namespace etl
#endif