|
|
@@ -8,7 +8,7 @@
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
-#include <stdexcept>
|
|
|
+#include <type_traits>
|
|
|
|
|
|
#include <math/clamp.h>
|
|
|
|
|
|
@@ -18,33 +18,42 @@ struct {
|
|
|
} assert_bounds;
|
|
|
using AssertBounds = decltype(assert_bounds);
|
|
|
|
|
|
-template <typename T, T MINIMUM_VALUE, T MAXIMUM_VALUE> class Bound final {
|
|
|
+template <typename T, auto MINIMUM_VALUE, auto MAXIMUM_VALUE> class Bound final {
|
|
|
public:
|
|
|
static_assert(MINIMUM_VALUE <= MAXIMUM_VALUE,
|
|
|
"The minimum value must be less than or equal to the maximum");
|
|
|
|
|
|
using value_type = T;
|
|
|
using underlying_type = value_type;
|
|
|
+ using bound_type =
|
|
|
+ std::common_type_t<decltype(MAXIMUM_VALUE), decltype(MINIMUM_VALUE)>;
|
|
|
|
|
|
- static constexpr const value_type min = MINIMUM_VALUE;
|
|
|
- static constexpr const value_type max = MAXIMUM_VALUE;
|
|
|
+ static constexpr const bound_type min = MINIMUM_VALUE;
|
|
|
+ static constexpr const bound_type max = MAXIMUM_VALUE;
|
|
|
|
|
|
private:
|
|
|
- value_type value{min};
|
|
|
+ value_type value_;
|
|
|
|
|
|
public:
|
|
|
- constexpr Bound() = default;
|
|
|
+ constexpr Bound() : value_(clamp(value_type(), min, max)) {}
|
|
|
constexpr Bound(value_type const & val) noexcept
|
|
|
- : value(clamp(val, min, max)) {}
|
|
|
+ : value_(clamp(val, min, max)) {}
|
|
|
|
|
|
- Bound(AssertBounds, value_type val) : value(val) {
|
|
|
- if (val < min || max < val) {
|
|
|
- throw std::out_of_range{"Must construct a value within range"};
|
|
|
- }
|
|
|
- }
|
|
|
+ Bound(AssertBounds, value_type val) : value_(assert_in_bounds(val, min, max)) {}
|
|
|
+
|
|
|
+ operator value_type() const { return value_; }
|
|
|
+ value_type operator*() const { return value_; }
|
|
|
+ value_type const *operator->() const { return &value_; }
|
|
|
|
|
|
- Bound & operator-=(value_type by) { return *this = Bound(value - by); }
|
|
|
- Bound & operator+=(value_type by) { return *this = Bound(value + by); }
|
|
|
+ auto operator<=>(Bound const &other) const noexcept = default;
|
|
|
+
|
|
|
+ template <typename F> void mutate(F &&func) {
|
|
|
+ std::forward<F>(func)(value_);
|
|
|
+ *this = Bound(value_);
|
|
|
+ }
|
|
|
+
|
|
|
+ Bound & operator-=(value_type by) { return *this = Bound(value_ - by); }
|
|
|
+ Bound & operator+=(value_type by) { return *this = Bound(value_ + by); }
|
|
|
Bound operator+(value_type by) const { return Bound{*this} += by; }
|
|
|
Bound operator-(value_type by) const { return Bound{*this} -= by; }
|
|
|
Bound & operator--() { return *this -= 1; }
|
|
|
@@ -61,32 +70,62 @@ public:
|
|
|
operator++();
|
|
|
return tmp;
|
|
|
}
|
|
|
-
|
|
|
- operator value_type() const { return value; }
|
|
|
};
|
|
|
|
|
|
-template <typename T, T MINMAX> using SymBound = Bound<T, -MINMAX, +MINMAX>;
|
|
|
-template <typename T, T VAL>
|
|
|
+template <typename T, auto MINMAX> using SymBound = Bound<T, -MINMAX, +MINMAX>;
|
|
|
+template <typename T, auto VAL>
|
|
|
using UniBound =
|
|
|
std::conditional_t<0 <= VAL, Bound<T, 0, VAL>, Bound<T, VAL, 0>>;
|
|
|
|
|
|
-template <typename L, L l1, L h1, typename R, R l2, R h2>
|
|
|
-auto operator+(Bound<L, l1, h1> lhs, Bound<R, l2, h2> rhs) {
|
|
|
- return L(lhs) + R(rhs);
|
|
|
+template <typename T, auto MIN, auto MAX, typename R>
|
|
|
+auto operator+(Bound<T, MIN, MAX> const &lhs, R const &rhs) {
|
|
|
+ return *lhs + rhs;
|
|
|
+}
|
|
|
+
|
|
|
+template <typename L, auto L_MIN, auto L_MAX, typename R, auto R_MIN, auto R_MAX>
|
|
|
+auto operator+(Bound<L, L_MIN, L_MAX> const &lhs, Bound<R, R_MIN, R_MAX> const &rhs) {
|
|
|
+ return *lhs + *rhs;
|
|
|
+}
|
|
|
+
|
|
|
+template <typename T, auto MIN, auto MAX, typename R>
|
|
|
+auto operator-(Bound<T, MIN, MAX> const &lhs, R const &rhs) {
|
|
|
+ return *lhs - rhs;
|
|
|
+}
|
|
|
+
|
|
|
+template <typename L, auto L_MIN, auto L_MAX, typename R, auto R_MIN, auto R_MAX>
|
|
|
+auto operator-(Bound<L, L_MIN, L_MAX> const &lhs, Bound<R, R_MIN, R_MAX> const &rhs) {
|
|
|
+ return *lhs - *rhs;
|
|
|
+}
|
|
|
+
|
|
|
+template <typename T, auto MIN, auto MAX, typename R>
|
|
|
+auto operator*(Bound<T, MIN, MAX> const &lhs, R const &rhs) {
|
|
|
+ return *lhs * rhs;
|
|
|
+}
|
|
|
+
|
|
|
+template <typename L, auto L_MIN, auto L_MAX, typename R, auto R_MIN, auto R_MAX>
|
|
|
+auto operator*(Bound<L, L_MIN, L_MAX> const &lhs, Bound<R, R_MIN, R_MAX> const &rhs) {
|
|
|
+ return *lhs * *rhs;
|
|
|
+}
|
|
|
+
|
|
|
+template <typename T, auto MIN, auto MAX, typename R>
|
|
|
+auto operator/(Bound<T, MIN, MAX> const &lhs, R const &rhs) {
|
|
|
+ return *lhs / rhs;
|
|
|
}
|
|
|
|
|
|
-template <typename L, L l1, L h1, typename R, R l2, R h2>
|
|
|
-auto operator-(Bound<L, l1, h1> lhs, Bound<R, l2, h2> rhs) {
|
|
|
- return L(lhs) - R(rhs);
|
|
|
+template <typename L, auto L_MIN, auto L_MAX, typename R, auto R_MIN, auto R_MAX>
|
|
|
+auto operator/(Bound<L, L_MIN, L_MAX> const &lhs, Bound<R, R_MIN, R_MAX> const &rhs) {
|
|
|
+ return *lhs / *rhs;
|
|
|
}
|
|
|
|
|
|
-template <typename L, L l1, L h1, typename R, R l2, R h2>
|
|
|
-auto operator*(Bound<L, l1, h1> lhs, Bound<R, l2, h2> rhs) {
|
|
|
- return L(lhs) * R(rhs);
|
|
|
+template <typename T, auto MIN, auto MAX, typename R>
|
|
|
+auto min(Bound<T, MIN, MAX> const &lhs, R const &rhs) {
|
|
|
+ using std::min;
|
|
|
+ return min(*lhs, rhs);
|
|
|
}
|
|
|
|
|
|
-template <typename L, L l1, L h1, typename R, R l2, R h2>
|
|
|
-auto operator/(Bound<L, l1, h1> lhs, Bound<R, l2, h2> rhs) {
|
|
|
- return L(lhs) / R(rhs);
|
|
|
+template <typename T, auto MIN, auto MAX, typename R>
|
|
|
+auto max(Bound<T, MIN, MAX> const &lhs, R const &rhs) {
|
|
|
+ using std::max;
|
|
|
+ return max(*lhs, rhs);
|
|
|
}
|
|
|
}
|