#pragma once #include #include namespace stdx { inline namespace v1 { template struct rollover_t { static_assert(unsigned_integral, "Argument to rollover_t must be an unsigned integral type."); using underlying_t = T; constexpr rollover_t() = default; template >> constexpr explicit rollover_t(U u) : value{static_cast(u)} {} template >> constexpr explicit rollover_t(rollover_t u) : rollover_t{static_cast(u)} {} [[nodiscard]] constexpr auto as_underlying() const -> underlying_t { return value; } constexpr explicit operator underlying_t() const { return value; } [[nodiscard]] constexpr auto operator+() const -> rollover_t { return *this; } [[nodiscard]] constexpr auto operator-() const -> rollover_t { return rollover_t{static_cast(-value)}; } constexpr auto operator++() -> rollover_t & { ++value; return *this; } constexpr auto operator++(int) -> rollover_t { return rollover_t{value++}; } constexpr auto operator--() -> rollover_t & { --value; return *this; } constexpr auto operator--(int) -> rollover_t { return rollover_t{value--}; } constexpr auto operator+=(rollover_t other) -> rollover_t & { value += other.value; return *this; } constexpr auto operator-=(rollover_t other) -> rollover_t & { value -= other.value; return *this; } constexpr auto operator*=(rollover_t other) -> rollover_t & { value *= other.value; return *this; } constexpr auto operator/=(rollover_t other) -> rollover_t & { value /= other.value; return *this; } constexpr auto operator%=(rollover_t other) -> rollover_t & { value %= other.value; return *this; } private: [[nodiscard]] constexpr friend auto operator==(rollover_t lhs, rollover_t rhs) -> bool { return lhs.value == rhs.value; } [[nodiscard]] constexpr friend auto operator!=(rollover_t lhs, rollover_t rhs) -> bool { return not(lhs == rhs); } constexpr friend auto operator<(rollover_t, rollover_t) -> bool = delete; constexpr friend auto operator<=(rollover_t, rollover_t) -> bool = delete; constexpr friend auto operator>(rollover_t, rollover_t) -> bool = delete; constexpr friend auto operator>=(rollover_t, rollover_t) -> bool = delete; [[nodiscard]] constexpr friend auto cmp_less(rollover_t lhs, rollover_t rhs) -> bool { constexpr auto mid = static_cast(~underlying_t{}) / 2; return static_cast(lhs.value - rhs.value) > mid; } [[nodiscard]] constexpr friend auto operator+(rollover_t lhs, rollover_t rhs) -> rollover_t { lhs += rhs; return lhs; } [[nodiscard]] constexpr friend auto operator-(rollover_t lhs, rollover_t rhs) -> rollover_t { lhs -= rhs; return lhs; } [[nodiscard]] constexpr friend auto operator*(rollover_t lhs, rollover_t rhs) -> rollover_t { lhs *= rhs; return lhs; } [[nodiscard]] constexpr friend auto operator/(rollover_t lhs, rollover_t rhs) -> rollover_t { lhs /= rhs; return lhs; } [[nodiscard]] constexpr friend auto operator%(rollover_t lhs, rollover_t rhs) -> rollover_t { lhs %= rhs; return lhs; } underlying_t value{}; }; template rollover_t(T) -> rollover_t; } // namespace v1 } // namespace stdx template struct std::common_type, stdx::rollover_t> { using type = stdx::rollover_t>; }; template struct std::common_type, I> { using type = stdx::rollover_t>>; };