Skip to content

Instantly share code, notes, and snippets.

@lhecker
Created January 7, 2020 17:51
Show Gist options
  • Save lhecker/f416036b53525072133f81ac3c8761f7 to your computer and use it in GitHub Desktop.
Save lhecker/f416036b53525072133f81ac3c8761f7 to your computer and use it in GitHub Desktop.
#include <algorithm>
#include <cstdint>
#include <numeric>
namespace impl
{
template<bool SmallInteger, bool Signed>
struct ClampedIntTypeHelper;
template<>
struct ClampedIntTypeHelper<false, false>
{
using Type = uint_fast64_t;
};
template<>
struct ClampedIntTypeHelper<false, true>
{
using Type = int_fast64_t;
};
template<>
struct ClampedIntTypeHelper<true, false>
{
using Type = unsigned int;
};
template<>
struct ClampedIntTypeHelper<true, true>
{
using Type = int;
};
template<typename T, typename U>
using ClampedIntPromote = typename impl::ClampedIntTypeHelper<sizeof(T) < sizeof(int) && sizeof(U) < sizeof(int), std::numeric_limits<T>::is_signed || std::numeric_limits<U>::is_signed>::Type;
} // namespace impl
template<typename T>
class ClampedInt
{
static_assert(sizeof(T) <= sizeof(int), "only int-sized types are supported");
public:
constexpr ClampedInt(T value) noexcept :
_value(value) {}
constexpr auto value() const noexcept -> T
{
return _value;
}
constexpr operator T() const
{
return _value;
}
template<typename U>
constexpr auto operator+(const U& rhs) const noexcept -> ClampedInt<T>
{
const auto rhsValue = ClampedInt<U>{rhs}.value();
using V = impl::ClampedIntPromote<T, decltype(rhsValue)>;
return { clamp(V{_value} + V{rhsValue}) };
}
template<typename U>
constexpr auto operator-(const U& rhs) const noexcept -> ClampedInt<T>
{
const auto rhsValue = ClampedInt<U>{rhs}.value();
using V = impl::ClampedIntPromote<T, decltype(rhsValue)>;
return { clamp(V{_value} - V{rhsValue}) };
}
template<typename U>
constexpr auto operator*(const U& rhs) const noexcept -> ClampedInt<T>
{
const auto rhsValue = ClampedInt<U>{rhs}.value();
using V = impl::ClampedIntPromote<T, decltype(rhsValue)>;
return { clamp(V{_value} * V{rhsValue}) };
}
template<typename U>
constexpr auto operator/(const U& rhs) const noexcept -> ClampedInt<T>
{
const auto rhsValue = ClampedInt<U>{rhs}.value();
using V = impl::ClampedIntPromote<T, decltype(rhsValue)>;
return { clamp(V{_value} / V{rhsValue}) };
}
template<typename U>
constexpr auto operator%(const U& rhs) const noexcept -> ClampedInt<T>
{
return { _value % T{rhs} };
}
template<typename U>
constexpr auto operator&(const U& rhs) const noexcept -> ClampedInt<T>
{
return { _value & T{rhs} };
}
template<typename U>
constexpr auto operator|(const U& rhs) const noexcept -> ClampedInt<T>
{
return { _value | T{rhs} };
}
template<typename U>
constexpr auto operator^(const U& rhs) const noexcept -> ClampedInt<T>
{
return { _value ^ T{rhs} };
}
template<typename U>
auto operator+=(const U& rhs) noexcept -> ClampedInt<T>&
{
*this = *this + rhs;
return *this;
}
template<typename U>
auto operator-=(const U& rhs) noexcept -> ClampedInt<T>&
{
*this = *this - rhs;
return *this;
}
template<typename U>
auto operator*=(const U& rhs) noexcept -> ClampedInt<T>&
{
*this = *this * rhs;
return *this;
}
template<typename U>
auto operator/=(const U& rhs) noexcept -> ClampedInt<T>&
{
*this = *this / rhs;
return *this;
}
template<typename U>
auto operator%=(const U& rhs) noexcept -> ClampedInt<T>&
{
*this = *this % rhs;
return *this;
}
template<typename U>
auto operator&=(const U& rhs) noexcept -> ClampedInt<T>&
{
*this = *this & rhs;
return *this;
}
template<typename U>
auto operator|=(const U& rhs) noexcept -> ClampedInt<T>&
{
*this = *this | rhs;
return *this;
}
template<typename U>
auto operator^=(const U& rhs) noexcept -> ClampedInt<T>&
{
*this = *this ^ rhs;
return *this;
}
private:
template<typename U>
static constexpr auto clamp(U value) noexcept -> T
{
constexpr U min = std::numeric_limits<T>::min();
constexpr U max = std::numeric_limits<T>::max();
return std::clamp(value, min, max);
}
T _value;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment