Verified Integer Types

Description

The library provides verified integer types that guarantee compile-time validation via consteval constructors and arithmetic. All values must be known at compile time — at runtime, verified types are read-only constants. They wrap any library_type (both u8-u128 and bounded_uint), adding a layer of compile-time-only enforcement on top of the existing safety guarantees.

Type Basis Type Underlying Type

verified_u8

u8

std::uint8_t

verified_u16

u16

std::uint16_t

verified_u32

u32

std::uint32_t

verified_u64

u64

std::uint64_t

verified_u128

u128

uint128_t

verified_bounded_integer<Min, Max>

bounded_uint<Min, Max>

smallest unsigned type fitting Max

Each type exposes an underlying_type member type alias that refers to the underlying fundamental integer type.

#include <boost/safe_numbers/verified_integers.hpp>

namespace boost::safe_numbers {

using verified_u8   = detail::verified_type_basis<u8>;
using verified_u16  = detail::verified_type_basis<u16>;
using verified_u32  = detail::verified_type_basis<u32>;
using verified_u64  = detail::verified_type_basis<u64>;
using verified_u128 = detail::verified_type_basis<u128>;

template <auto Min, auto Max>
using verified_bounded_integer = detail::verified_type_basis<bounded_uint<Min, Max>>;

template <library_type BasisType>
class verified_type_basis {

public:
    using underlying_type = underlying_type_t<BasisType>;

    // Construction (consteval -- compile-time only)
    explicit consteval verified_type_basis(const BasisType basis);
    explicit consteval verified_type_basis(const underlying_type val);

    // Conversion to basis type and underlying type (constexpr -- works at runtime)
    explicit constexpr operator BasisType() const noexcept;
    explicit constexpr operator underlying_type() const noexcept;

    // Comparison operators (constexpr -- works at runtime)
    friend constexpr auto operator<=>(verified_type_basis lhs, verified_type_basis rhs) noexcept
        -> std::strong_ordering = default;

    // Compound assignment operators (consteval -- compile-time only)
    consteval auto operator+=(verified_type_basis rhs) -> verified_type_basis&;
    consteval auto operator-=(verified_type_basis rhs) -> verified_type_basis&;
    consteval auto operator*=(verified_type_basis rhs) -> verified_type_basis&;
    consteval auto operator/=(verified_type_basis rhs) -> verified_type_basis&;
    consteval auto operator%=(verified_type_basis rhs) -> verified_type_basis&;

    // Increment and decrement operators (consteval -- compile-time only)
    consteval auto operator++() -> verified_type_basis&;
    consteval auto operator++(int) -> verified_type_basis;
    consteval auto operator--() -> verified_type_basis&;
    consteval auto operator--(int) -> verified_type_basis;

}; // class verified_type_basis

// Arithmetic operators (consteval -- compile-time only, overflow = compile error)
template <library_type BasisType>
consteval auto operator+(const verified_type_basis<BasisType> lhs,
                         const verified_type_basis<BasisType> rhs) -> verified_type_basis<BasisType>;

template <library_type BasisType>
consteval auto operator-(const verified_type_basis<BasisType> lhs,
                         const verified_type_basis<BasisType> rhs) -> verified_type_basis<BasisType>;

template <library_type BasisType>
consteval auto operator*(const verified_type_basis<BasisType> lhs,
                         const verified_type_basis<BasisType> rhs) -> verified_type_basis<BasisType>;

template <library_type BasisType>
consteval auto operator/(const verified_type_basis<BasisType> lhs,
                         const verified_type_basis<BasisType> rhs) -> verified_type_basis<BasisType>;

template <library_type BasisType>
consteval auto operator%(const verified_type_basis<BasisType> lhs,
                         const verified_type_basis<BasisType> rhs) -> verified_type_basis<BasisType>;

} // namespace boost::safe_numbers

Operator Behavior

Construction

explicit consteval verified_type_basis(const BasisType basis);
explicit consteval verified_type_basis(const underlying_type val);

Construction is consteval — it can only occur at compile time. The first overload accepts the safe integer basis type directly; the second accepts the underlying fundamental type and constructs the basis type internally. If the value is out of range (e.g., for a bounded type), the compilation fails.

Conversion

explicit constexpr operator BasisType() const noexcept;
explicit constexpr operator underlying_type() const noexcept;

Conversion to the basis type or the underlying fundamental type is constexpr and explicit. These work at runtime, allowing verified values to be used in runtime contexts such as stream output, to_chars, and comparisons.

Comparison Operators

friend constexpr auto operator<=>(verified_type_basis lhs, verified_type_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. Comparisons are constexpr and work at runtime.

Arithmetic Operators

template <library_type BasisType>
consteval auto operator+(const verified_type_basis<BasisType> lhs,
                         const verified_type_basis<BasisType> rhs) -> verified_type_basis<BasisType>;

template <library_type BasisType>
consteval auto operator-(const verified_type_basis<BasisType> lhs,
                         const verified_type_basis<BasisType> rhs) -> verified_type_basis<BasisType>;

template <library_type BasisType>
consteval auto operator*(const verified_type_basis<BasisType> lhs,
                         const verified_type_basis<BasisType> rhs) -> verified_type_basis<BasisType>;

template <library_type BasisType>
consteval auto operator/(const verified_type_basis<BasisType> lhs,
                         const verified_type_basis<BasisType> rhs) -> verified_type_basis<BasisType>;

template <library_type BasisType>
consteval auto operator%(const verified_type_basis<BasisType> lhs,
                         const verified_type_basis<BasisType> rhs) -> verified_type_basis<BasisType>;

All arithmetic operators are consteval — they can only be evaluated at compile time. The operations delegate to the underlying basis type’s operators, which perform overflow/underflow checking. If an overflow, underflow, or division-by-zero would occur, the compilation fails with a compiler error.

Compound Assignment Operators

consteval auto operator+=(verified_type_basis rhs) -> verified_type_basis&;
consteval auto operator-=(verified_type_basis rhs) -> verified_type_basis&;
consteval auto operator*=(verified_type_basis rhs) -> verified_type_basis&;
consteval auto operator/=(verified_type_basis rhs) -> verified_type_basis&;
consteval auto operator%=(verified_type_basis rhs) -> verified_type_basis&;

Compound assignment operators are consteval and delegate to the corresponding arithmetic operators. Overflow at compile time produces a compiler error.

Increment and Decrement Operators

consteval auto operator++() -> verified_type_basis&;
consteval auto operator++(int) -> verified_type_basis;
consteval auto operator--() -> verified_type_basis&;
consteval auto operator--(int) -> verified_type_basis;
  • ++ (pre/post): consteval. Produces a compile error if the value is already at the maximum.

  • -- (pre/post): consteval. Produces a compile error if the value is already at the minimum (zero for unbounded types, Min for bounded types).

Compile-Time vs Runtime

Operation Qualifier Compile-Time Runtime

Construction

consteval

Yes

No

Arithmetic (+, -, *, /, %)

consteval

Yes

No

Compound assignment (+=, -=, etc.)

consteval

Yes

No

Increment/decrement (++, --)

consteval

Yes

No

Conversion to BasisType / underlying_type

constexpr

Yes

Yes

Comparison (<⇒, ==, <, etc.)

constexpr

Yes

Yes

Mixed-type arithmetic (verified op basis)

constexpr

Yes

Yes

Mixed-type comparison (verified vs basis)

constexpr

Yes

Yes

Mixed-type bitwise (verified op basis)

constexpr

Yes

Yes

Mixed-Type Operations

Verified types can participate in runtime expressions when combined with their basis types. The result is always the runtime (basis) type, effectively treating the verified value as a read-only constant operand.

Arithmetic

// verified op basis -> basis
template <library_type VerifiedBasisType>
constexpr auto operator+(const verified_type_basis<VerifiedBasisType> lhs,
                         const VerifiedBasisType rhs) -> VerifiedBasisType;

// basis op verified -> basis
template <library_type VerifiedBasisType>
constexpr auto operator+(const VerifiedBasisType lhs,
                         const verified_type_basis<VerifiedBasisType> rhs) -> VerifiedBasisType;

All five arithmetic operators (+, -, *, /, %) are supported in both directions. Cross-width operations (e.g., verified_u32 + u64) are allowed and follow the same rules as the basis types.

Comparisons

template <library_type VerifiedBasisType>
constexpr auto operator<=>(const verified_type_basis<VerifiedBasisType> lhs,
                           const VerifiedBasisType rhs) -> std::strong_ordering;

template <library_type VerifiedBasisType>
constexpr auto operator==(const verified_type_basis<VerifiedBasisType> lhs,
                          const VerifiedBasisType rhs) -> bool;

Full three-way and equality comparisons are supported in both directions (verified vs basis, basis vs verified).

Bitwise Operations

template <non_bounded_unsigned_library_type VerifiedBasisType>
constexpr auto operator&(const verified_type_basis<VerifiedBasisType> lhs,
                         const VerifiedBasisType rhs) -> VerifiedBasisType;

Bitwise operators (&, |, ^, <<, >>) are supported for non-bounded unsigned types in both directions. Shift operators include overflow checking via throw_exception policy.

Type Safety

Mixed operations between verified types with different basis types produce clear static_assert errors:

constexpr auto a = verified_u32{u32{10}};
constexpr auto b = verified_u64{u64{20}};

auto c = a + b;  // error: "Can not perform addition between verified types with different basis types"
auto d = (a == b);  // error: "Can not compare verified types with different basis types"
Example 1. This example demonstrates mixed-type operations between verified constants and runtime values.
// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// Verified types are compile-time constants, but they can participate in
// runtime operations when mixed with their basis types. The result is
// always the runtime (basis) type, effectively treating the verified value
// as a read-only constant operand.

#include <boost/safe_numbers/verified_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>

int main()
{
    using namespace boost::safe_numbers;

    // A compile-time constant and a runtime value
    constexpr auto tax_rate = verified_u32{u32{20}};
    auto price = u32{500};

    // --- Arithmetic: verified op basis -> basis ---
    const auto tax = tax_rate * price / u32{100};
    std::cout << "Tax on " << price << " at " << tax_rate << "%: " << tax << '\n';

    // --- Arithmetic: basis op verified -> basis ---
    constexpr auto discount = verified_u32{u32{50}};
    const auto discounted = price - discount;
    std::cout << price << " - " << discount << " discount = " << discounted << '\n';

    std::cout << '\n';

    // --- Comparisons: verified vs basis ---
    constexpr auto threshold = verified_u32{u32{1000}};
    std::cout << std::boolalpha;
    std::cout << price << " < " << threshold << ": " << (price < threshold) << '\n';
    std::cout << threshold << " > " << price << ": " << (threshold > price) << '\n';
    std::cout << price << " == " << threshold << ": " << (price == threshold) << '\n';

    std::cout << '\n';

    // --- Bitwise operations: verified vs basis ---
    constexpr auto mask = verified_u32{u32{0xFF}};
    auto value = u32{0xABCD};
    const auto masked = value & mask;
    std::cout << "0xABCD & 0xFF = " << masked << '\n';

    const auto combined = mask | value;
    std::cout << "0xFF | 0xABCD = " << combined << '\n';

    // Shift operations
    constexpr auto shift = verified_u32{u32{4}};
    const auto shifted = value << shift;
    std::cout << "0xABCD << 4 = " << shifted << '\n';

    std::cout << '\n';

    // --- Bounded types work too ---
    constexpr auto bounded_offset = verified_bounded_integer<0u, 100u>{10u};
    auto bounded_val = bounded_uint<0u, 100u>{50u};
    const auto bounded_sum = bounded_val + bounded_offset;
    std::cout << "bounded 50 + 10 = " << bounded_sum << '\n';

    const auto bounded_diff = bounded_val - bounded_offset;
    std::cout << "bounded 50 - 10 = " << bounded_diff << '\n';

    // Bounded comparisons
    std::cout << "bounded 50 > 10: " << (bounded_val > bounded_offset) << '\n';

    return 0;
}

Output:

Tax on 500 at 20%: 100
500 - 50 discount = 450

500 < 1000: true
1000 > 500: true
500 == 1000: false

0xABCD & 0xFF = 205
0xFF | 0xABCD = 44031
0xABCD << 4 = 703696

bounded 50 + 10 = 60
bounded 50 - 10 = 40
bounded 50 > 10: true

Integration with Other Features

Verified types integrate with the library’s other features:

  • <bit> Support: Functions returning int or bool (has_single_bit, bit_width, countl_zero, countl_one, countr_zero, countr_one, popcount) work at runtime with verified types via their constexpr conversion operator. Functions returning the safe type (bit_ceil, bit_floor, rotl, rotr, byteswap) have consteval overloads for verified types.

  • <charconv> Support: to_chars works at runtime (only reads the value). from_chars has a consteval overload for verified types.

  • Stream I/O Support: operator<< works normally at runtime. operator>> is excluded for verified types since they cannot be constructed at runtime.

  • <limits> Support: std::numeric_limits is fully specialized for all verified types, delegating to the basis type’s limits. For verified bounded types, min() and max() correctly report the bounded range.

  • Formatting Support: Both std::format and {fmt} work at runtime via the constexpr conversion operator.