Unsigned Integer Types
Description
The library provides safe unsigned integer types that detect overflow, underflow, and other undefined behavior at runtime. These types are drop-in replacements for the standard unsigned integer types with added safety guarantees.
| Type | Underlying Type | Width | Min | Max |
|---|---|---|---|---|
|
|
8 bits |
0 |
255 |
|
|
16 bits |
0 |
65,535 |
|
|
32 bits |
0 |
4,294,967,295 |
|
|
64 bits |
0 |
18,446,744,073,709,551,615 |
|
|
128 bits |
0 |
340,282,366,920,938,463,463,374,607,431,768,211,455 |
Each type exposes a basis_type member type alias that refers to the underlying integer type, allowing conversion back to built-in types when needed.
#include <boost/safe_numbers/unsigned_integers.hpp>
namespace boost::safe_numbers {
using u8 = detail::unsigned_integer_basis<std::uint8_t>;
using u16 = detail::unsigned_integer_basis<std::uint16_t>;
using u32 = detail::unsigned_integer_basis<std::uint32_t>;
using u64 = detail::unsigned_integer_basis<std::uint64_t>;
using u128 = detail::unsigned_integer_basis<int128::uint128_t>;
template <unsigned_integral BasisType>
class unsigned_integer_basis {
public:
using basis_type = BasisType;
// Construction
constexpr unsigned_integer_basis() noexcept = default;
explicit constexpr unsigned_integer_basis(BasisType val) noexcept;
template <typename T>
requires std::is_same_v<T, bool>
explicit constexpr unsigned_integer_basis(T) noexcept = delete; // bool prohibited
// Conversion to underlying types
template <unsigned_integral OtherBasis>
explicit constexpr operator OtherBasis() const noexcept;
// Comparison operators
friend constexpr auto operator<=>(unsigned_integer_basis lhs, unsigned_integer_basis rhs) noexcept
-> std::strong_ordering = default;
// Compound assignment operators
template <unsigned_integral OtherBasis>
constexpr auto operator+=(unsigned_integer_basis<OtherBasis> rhs) -> unsigned_integer_basis&;
template <unsigned_integral OtherBasis>
constexpr auto operator-=(unsigned_integer_basis<OtherBasis> rhs) -> unsigned_integer_basis&;
template <unsigned_integral OtherBasis>
constexpr auto operator*=(unsigned_integer_basis<OtherBasis> rhs) -> unsigned_integer_basis&;
template <unsigned_integral OtherBasis>
constexpr auto operator/=(unsigned_integer_basis<OtherBasis> rhs) -> unsigned_integer_basis&;
template <unsigned_integral OtherBasis>
constexpr auto operator%=(unsigned_integer_basis<OtherBasis> rhs) -> unsigned_integer_basis&;
// Increment and decrement operators
constexpr auto operator++() -> unsigned_integer_basis&;
constexpr auto operator++(int) -> unsigned_integer_basis;
constexpr auto operator--() -> unsigned_integer_basis&;
constexpr auto operator--(int) -> unsigned_integer_basis;
}; // class unsigned_integer_basis
// Arithmetic operators (throw on overflow/underflow)
template <unsigned_integral BasisType>
constexpr auto operator+(unsigned_integer_basis<BasisType> lhs,
unsigned_integer_basis<BasisType> rhs) -> unsigned_integer_basis<BasisType>;
template <unsigned_integral BasisType>
constexpr auto operator-(unsigned_integer_basis<BasisType> lhs,
unsigned_integer_basis<BasisType> rhs) -> unsigned_integer_basis<BasisType>;
template <unsigned_integral BasisType>
constexpr auto operator*(unsigned_integer_basis<BasisType> lhs,
unsigned_integer_basis<BasisType> rhs) -> unsigned_integer_basis<BasisType>;
template <unsigned_integral BasisType>
constexpr auto operator/(unsigned_integer_basis<BasisType> lhs,
unsigned_integer_basis<BasisType> rhs) -> unsigned_integer_basis<BasisType>;
template <unsigned_integral BasisType>
constexpr auto operator%(unsigned_integer_basis<BasisType> lhs,
unsigned_integer_basis<BasisType> rhs) -> unsigned_integer_basis<BasisType>;
// Saturating arithmetic (clamp to min/max on overflow/underflow)
template <UnsignedLibType T>
constexpr T saturating_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T saturating_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T saturating_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T saturating_div(T lhs, T rhs);
template <UnsignedLibType T>
constexpr T saturating_mod(T lhs, T rhs);
// Overflowing arithmetic (wrap and return overflow flag)
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_div(T lhs, T rhs);
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_mod(T lhs, T rhs);
// Checked arithmetic (return std::nullopt on overflow/underflow)
template <UnsignedLibType T>
constexpr std::optional<T> checked_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_div(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_mod(T lhs, T rhs) noexcept;
// Wrapping arithmetic (wrap without indication)
template <UnsignedLibType T>
constexpr T wrapping_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T wrapping_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T wrapping_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T wrapping_div(T lhs, T rhs);
template <UnsignedLibType T>
constexpr T wrapping_mod(T lhs, T rhs);
// Strict arithmetic (call std::exit(EXIT_FAILURE) on error)
template <UnsignedLibType T>
constexpr T strict_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_div(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_mod(T lhs, T rhs) noexcept;
} // namespace boost::safe_numbers
Operator Behavior
Default Construction
constexpr unsigned_integer_basis() noexcept = default;
Values are default-initialized to zero.
Construction from Underlying Type
explicit constexpr unsigned_integer_basis(BasisType val) noexcept;
Construction from the underlying type is explicit to prevent accidental conversions.
Construction from bool
template <typename T>
requires std::is_same_v<T, bool>
explicit constexpr unsigned_integer_basis(T) noexcept = delete;
Constructing from bool is a compile-time error.
Conversion to Underlying Types
template <unsigned_integral OtherBasis>
explicit constexpr operator OtherBasis() const noexcept;
Conversion to other unsigned integral types is explicit. Narrowing conversions cause a compile-time error.
Comparison Operators
friend constexpr auto operator<=>(unsigned_integer_basis lhs, unsigned_integer_basis rhs) noexcept
-> std::strong_ordering = default;
Full three-way comparison is supported via operator<=>, which returns std::strong_ordering.
All comparison operators (<, ⇐, >, >=, ==, !=) are available.
Arithmetic Operators
template <unsigned_integral BasisType>
constexpr auto operator+(unsigned_integer_basis<BasisType> lhs,
unsigned_integer_basis<BasisType> rhs) -> unsigned_integer_basis<BasisType>;
template <unsigned_integral BasisType>
constexpr auto operator-(unsigned_integer_basis<BasisType> lhs,
unsigned_integer_basis<BasisType> rhs) -> unsigned_integer_basis<BasisType>;
template <unsigned_integral BasisType>
constexpr auto operator*(unsigned_integer_basis<BasisType> lhs,
unsigned_integer_basis<BasisType> rhs) -> unsigned_integer_basis<BasisType>;
template <unsigned_integral BasisType>
constexpr auto operator/(unsigned_integer_basis<BasisType> lhs,
unsigned_integer_basis<BasisType> rhs) -> unsigned_integer_basis<BasisType>;
template <unsigned_integral BasisType>
constexpr auto operator%(unsigned_integer_basis<BasisType> lhs,
unsigned_integer_basis<BasisType> rhs) -> unsigned_integer_basis<BasisType>;
All arithmetic operators perform runtime checks and throw exceptions when undefined behavior would occur:
-
+: Throwsstd::overflow_errorif the result exceeds the maximum representable value -
-: Throwsstd::underflow_errorif the result would be negative (wrap around) -
*: Throwsstd::overflow_errorif the result exceeds the maximum representable value -
/: Throwsstd::domain_errorif dividing by zero -
%: Throwsstd::domain_errorif the divisor is zero
Compound Assignment Operators
template <unsigned_integral OtherBasis>
constexpr auto operator+=(unsigned_integer_basis<OtherBasis> rhs) -> unsigned_integer_basis&;
template <unsigned_integral OtherBasis>
constexpr auto operator-=(unsigned_integer_basis<OtherBasis> rhs) -> unsigned_integer_basis&;
template <unsigned_integral OtherBasis>
constexpr auto operator*=(unsigned_integer_basis<OtherBasis> rhs) -> unsigned_integer_basis&;
template <unsigned_integral OtherBasis>
constexpr auto operator/=(unsigned_integer_basis<OtherBasis> rhs) -> unsigned_integer_basis&;
template <unsigned_integral OtherBasis>
constexpr auto operator%=(unsigned_integer_basis<OtherBasis> rhs) -> unsigned_integer_basis&;
Compound assignment operators follow the same exception behavior as their corresponding arithmetic operators.
Increment and Decrement Operators
constexpr auto operator++() -> unsigned_integer_basis&;
constexpr auto operator++(int) -> unsigned_integer_basis;
constexpr auto operator--() -> unsigned_integer_basis&;
constexpr auto operator--(int) -> unsigned_integer_basis;
-
++(pre/post): Throwsstd::overflow_errorif the value is already at the maximum -
--(pre/post): Throwsstd::underflow_errorif the value is already zero
Alternative Arithmetic Functions
For cases where throwing exceptions is not desired, alternative arithmetic functions are provided with different overflow handling policies.
Saturating Arithmetic
template <UnsignedLibType T>
constexpr T saturating_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T saturating_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T saturating_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T saturating_div(T lhs, T rhs);
template <UnsignedLibType T>
constexpr T saturating_mod(T lhs, T rhs);
These functions clamp the result to the representable range instead of throwing:
-
saturating_add: Returns the sum, saturating atstd::numeric_limits<T>::max()on overflow -
saturating_sub: Returns the difference, saturating atstd::numeric_limits<T>::min()(zero) on underflow -
saturating_mul: Returns the product, saturating atstd::numeric_limits<T>::max()on overflow -
saturating_div: Returns the quotient; throwsstd::domain_erroron division by zero (overflow is impossible) -
saturating_mod: Returns the remainder; throwsstd::domain_erroron division by zero (overflow is impossible)
Overflowing Arithmetic
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_div(T lhs, T rhs);
template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_mod(T lhs, T rhs);
These functions provide well-defined wrapping semantics with a flag to indicate if overflow occurred.
This follows normal C family unsigned rollover where UINT_MAX + 1 == 0 and 0 - 1 == UINT_MAX.
-
overflowing_add: Returns the wrapped sum andtrueif overflow occurred -
overflowing_sub: Returns the wrapped difference andtrueif underflow occurred -
overflowing_mul: Returns the wrapped product andtrueif overflow occurred -
overflowing_div: Returns the quotient andfalse; throwsstd::domain_erroron division by zero -
overflowing_mod: Returns the remainder andfalse; throwsstd::domain_erroron division by zero
Checked Arithmetic
template <UnsignedLibType T>
constexpr std::optional<T> checked_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_div(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr std::optional<T> checked_mod(T lhs, T rhs) noexcept;
These functions return std::nullopt on overflow, underflow, or division by zero:
-
checked_add: Returns the sum, orstd::nullopton overflow -
checked_sub: Returns the difference, orstd::nullopton underflow -
checked_mul: Returns the product, orstd::nullopton overflow -
checked_div: Returns the quotient, orstd::nullopton division by zero -
checked_mod: Returns the remainder, orstd::nullopton division by zero
Wrapping Arithmetic
template <UnsignedLibType T>
constexpr T wrapping_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T wrapping_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T wrapping_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T wrapping_div(T lhs, T rhs);
template <UnsignedLibType T>
constexpr T wrapping_mod(T lhs, T rhs);
These functions wrap on overflow without any indication:
-
wrapping_add: Returns the wrapped sum -
wrapping_sub: Returns the wrapped difference -
wrapping_mul: Returns the wrapped product -
wrapping_div: Returns the quotient; throwsstd::domain_erroron division by zero -
wrapping_mod: Returns the remainder; throwsstd::domain_erroron division by zero
Strict Arithmetic
template <UnsignedLibType T>
constexpr T strict_add(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_sub(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_mul(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_div(T lhs, T rhs) noexcept;
template <UnsignedLibType T>
constexpr T strict_mod(T lhs, T rhs) noexcept;
These functions call std::exit(EXIT_FAILURE) on error, providing a hard termination policy for safety-critical applications where exceptions cannot be used:
-
strict_add: Returns the sum; callsstd::exit(EXIT_FAILURE)on overflow -
strict_sub: Returns the difference; callsstd::exit(EXIT_FAILURE)on underflow -
strict_mul: Returns the product; callsstd::exit(EXIT_FAILURE)on overflow -
strict_div: Returns the quotient; callsstd::exit(EXIT_FAILURE)on division by zero -
strict_mod: Returns the remainder; callsstd::exit(EXIT_FAILURE)on modulo by zero
All strict functions are marked noexcept since std::exit does not throw.
Exception Summary
| Operation | Exception Type | Condition |
|---|---|---|
|
|
Result exceeds maximum value |
|
|
Result would be negative |
|
|
Result exceeds maximum value |
|
|
Division by zero |
|
|
Modulo by zero |
|
|
Value is at maximum |
|
|
Value is zero |
|
|
Division by zero |
|
|
Division by zero |
|
|
Division by zero |
Strict Functions Behavior
The strict_* functions do not throw exceptions. Instead, they call std::exit(EXIT_FAILURE) on error:
| Operation | Behavior | Condition |
|---|---|---|
|
|
Overflow |
|
|
Underflow |
|
|
Overflow |
|
|
Division by zero |
|
|
Modulo by zero |
Policy Summary
| Policy | Overflow/Underflow Behavior | Division by Zero | noexcept |
|---|---|---|---|
Default operators |
Throws exception |
Throws |
No |
|
Clamps to min/max |
Throws |
Add/Sub/Mul: Yes, Div/Mod: No |
|
Wraps, returns flag |
Throws |
Add/Sub/Mul: Yes, Div/Mod: No |
|
Returns |
Returns |
Yes |
|
Wraps silently |
Throws |
Add/Sub/Mul: Yes, Div/Mod: No |
|
Calls |
Calls |
Yes |