Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2cb011b
Fix missing spaceship operator causing incorrect comparisons
mborland Feb 6, 2026
7d64248
Add new type traits
mborland Feb 6, 2026
e168961
Add a bounded unsigned integer class
mborland Feb 6, 2026
49b89e4
Add testing of rudamentary features
mborland Feb 6, 2026
f8358f9
Improve concepts
mborland Feb 6, 2026
27a6025
Fix construction of values that are out of bounds
mborland Feb 6, 2026
21ef354
Add testing of boundary
mborland Feb 6, 2026
9f4f205
Add basic addition operator
mborland Feb 6, 2026
e622faf
Add testing of basic addition operator
mborland Feb 6, 2026
2ecf8f9
Add other basic operators and testing
mborland Feb 6, 2026
b75112a
Allow conversions between different bounded integer types
mborland Feb 6, 2026
102c3dd
Test conversions between different bounded integer types
mborland Feb 6, 2026
cee471b
Add macro for mixed type ops
mborland Feb 6, 2026
dd81ebc
Add testing of mixed type ops
mborland Feb 6, 2026
bdfd1dc
Improve behavior of mixed conversion operators
mborland Feb 6, 2026
832fbaf
Reject bool bounds to bounded integer
mborland Feb 6, 2026
ed5fccc
Add compound operations
mborland Feb 9, 2026
caa3fa2
Add bounded_uint docs
mborland Feb 9, 2026
826e5e5
Add type_traits for bounded integers
mborland Feb 9, 2026
e6ccd99
Make charconv more generic
mborland Feb 9, 2026
5715a02
Remove now unneeded headers
mborland Feb 9, 2026
2601521
Inject detail operators into higher namespace
mborland Feb 9, 2026
54ffd7d
Reorganize forward declarations
mborland Feb 9, 2026
dcc4306
Add conversion to hardware type
mborland Feb 9, 2026
8582329
Add conversion to and construction from hardware type
mborland Feb 9, 2026
74dc2f0
Add support for bounded integers
mborland Feb 9, 2026
920994a
Test bounded integer charconv support
mborland Feb 9, 2026
768464e
Fix support and test bit with bounded_uint
mborland Feb 9, 2026
406946b
Fix support for formatting of bounded integers
mborland Feb 9, 2026
678979c
Add support for and test bounded uint limits
mborland Feb 9, 2026
e166f50
Add bounded limits example
mborland Feb 9, 2026
604d61a
Inline example into limits page
mborland Feb 9, 2026
1dceee4
Update charconv docs and page
mborland Feb 9, 2026
82dea09
Update format example and doc page
mborland Feb 9, 2026
b9a154e
Remove bounded support for rotl/rotr/byteswap
mborland Feb 9, 2026
5dc7791
Fix MSVC C4244 warning
mborland Feb 9, 2026
f314210
Fix GCC 11 and 12 compile error
mborland Feb 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
** xref:api_reference.adoc#api_headers[Headers]
* xref:policies.adoc[]
* xref:unsigned_integers.adoc[]
* xref:bounded_uint.adoc[]
* xref:literals.adoc[]
* xref:limits.adoc[]
* xref:format.adoc[]
Expand Down
13 changes: 13 additions & 0 deletions doc/modules/ROOT/pages/api_reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ https://www.boost.org/LICENSE_1_0.txt
| Safe unsigned 128-bit integer
|===

=== Bounded Unsigned Integer Type

[cols="1,2", options="header"]
|===
| Type | Description

| xref:bounded_uint.adoc[`bounded_uint<Min, Max>`]
| Safe unsigned integer constrained to a compile-time range `[Min, Max]`
|===

=== Enumerations

[cols="1,2", options="header"]
Expand Down Expand Up @@ -184,4 +194,7 @@ This header is not included in the convenience header since it requires external

| `<boost/safe_numbers/unsigned_integers.hpp>`
| All unsigned safe integer types (`u8`, `u16`, `u32`, `u64`, `u128`)

| `<boost/safe_numbers/bounded_integers.hpp>`
| Bounded unsigned integer type (`bounded_uint<Min, Max>`)
|===
22 changes: 18 additions & 4 deletions doc/modules/ROOT/pages/bit.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ https://www.boost.org/LICENSE_1_0.txt

== Description

The library provides thin wrappers around the C++20 https://en.cppreference.com/w/cpp/header/bit.html[`<bit>`] functions for safe integer types.
The library provides thin wrappers around the C++20 https://en.cppreference.com/w/cpp/header/bit.html[`<bit>`] functions for all safe integer types, including `bounded_uint<Min, Max>`.
Each function extracts the underlying value, delegates to the corresponding `std::` function, and wraps the result back into the safe type where appropriate.
For `u128`, the functions delegate to the `boost::int128` implementations.

Expand Down Expand Up @@ -73,24 +73,28 @@ See https://en.cppreference.com/w/cpp/numeric/bit_width.html[`std::bit_width`].

[source,c++]
----
template <unsigned_library_type UnsignedInt>
template <non_bounded_unsigned_library_type UnsignedInt>
constexpr auto rotl(UnsignedInt x, int s) noexcept -> UnsignedInt;
----

Computes the result of bitwise left-rotating `x` by `s` positions.
See https://en.cppreference.com/w/cpp/numeric/rotl.html[`std::rotl`].

NOTE: `rotl` is not available for `bounded_uint` types. Bit rotation can produce values outside the valid bounded range, making the operation semantically meaningless for bounded integers.

=== rotr

[source,c++]
----
template <unsigned_library_type UnsignedInt>
template <non_bounded_unsigned_library_type UnsignedInt>
constexpr auto rotr(UnsignedInt x, int s) noexcept -> UnsignedInt;
----

Computes the result of bitwise right-rotating `x` by `s` positions.
See https://en.cppreference.com/w/cpp/numeric/rotr.html[`std::rotr`].

NOTE: `rotr` is not available for `bounded_uint` types. Bit rotation can produce values outside the valid bounded range, making the operation semantically meaningless for bounded integers.

== Counting Functions

=== countl_zero
Expand Down Expand Up @@ -154,7 +158,7 @@ See https://en.cppreference.com/w/cpp/numeric/popcount.html[`std::popcount`].

[source,c++]
----
template <unsigned_library_type UnsignedInt>
template <non_bounded_unsigned_library_type UnsignedInt>
constexpr auto byteswap(UnsignedInt x) noexcept -> UnsignedInt;
----

Expand All @@ -163,6 +167,8 @@ For standard unsigned types this delegates to `std::byteswap` (C++23).
For `u128` this delegates to the `boost::int128::byteswap` implementation.
See https://en.cppreference.com/w/cpp/numeric/byteswap.html[`std::byteswap`].

NOTE: `byteswap` is not available for `bounded_uint` types. Byte reversal can produce values outside the valid bounded range, making the operation semantically meaningless for bounded integers.

== Examples

.This https://github.com/boostorg/safe_numbers/blob/develop/examples/bit.cpp[example] demonstrates the bit manipulation functions.
Expand Down Expand Up @@ -190,5 +196,13 @@ countr_one(0x0F00) = 0
popcount(0x0F00) = 4

byteswap(0x12345678) = 0x78563412

bounded has_single_bit(40) = 0
bounded bit_ceil(40) = 64
bounded bit_floor(40) = 32
bounded bit_width(40) = 6
bounded popcount(40) = 2
bounded countl_zero(0x0F00) = 4
bounded popcount(0x0F00) = 4
----
====
213 changes: 213 additions & 0 deletions doc/modules/ROOT/pages/bounded_uint.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
////
Copyright 2026 Matt Borland
Distributed under the Boost Software License, Version 1.0.
https://www.boost.org/LICENSE_1_0.txt
////

[#bounded_uint]

= Bounded Unsigned Integer Type

== Description

The `bounded_uint<Min, Max>` class template provides a safe unsigned integer type whose values are constrained to a compile-time range `[Min, Max]`.
Any operation that would produce a value outside this range throws an exception at runtime.

The underlying storage type (`basis_type`) is automatically selected as the smallest safe unsigned integer type capable of representing `Max`:

|===
| Max Value Range | Selected `basis_type`

| 0 -- 255 | `u8`
| 256 -- 65,535 | `u16`
| 65,536 -- 4,294,967,295 | `u32`
| 4,294,967,296 -- 2^64 - 1 | `u64`
| 2^64 -- 2^128 - 1 | `u128`
|===

The template parameters `Min` and `Max` can be any non-`bool` unsigned type, including the library's own safe unsigned types (`u8`, `u16`, etc.) or built-in unsigned types (`std::uint8_t`, `unsigned int`, etc.).
`Max` must be strictly greater than `Min`.

[source,c++]
----
#include <boost/safe_numbers/bounded_integers.hpp>

namespace boost::safe_numbers {

template <auto Min, auto Max>
requires (detail::valid_bound<decltype(Min)> &&
detail::valid_bound<decltype(Max)> &&
detail::raw_value(Max) > detail::raw_value(Min))
class bounded_uint
{
public:
using basis_type = /* smallest safe unsigned type that fits Max */;

// Construction
explicit constexpr bounded_uint(basis_type val);

// Conversion to underlying types
template <typename OtherBasis>
explicit constexpr operator OtherBasis() const;

// Conversion to other bounded_uint types
template <auto Min2, auto Max2>
explicit constexpr operator bounded_uint<Min2, Max2>() const;

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

// Compound assignment operators
constexpr auto operator+=(bounded_uint rhs) -> bounded_uint&;
constexpr auto operator-=(bounded_uint rhs) -> bounded_uint&;
constexpr auto operator*=(bounded_uint rhs) -> bounded_uint&;
constexpr auto operator/=(bounded_uint rhs) -> bounded_uint&;

// Increment and decrement operators
constexpr auto operator++() -> bounded_uint&;
constexpr auto operator++(int) -> bounded_uint;
constexpr auto operator--() -> bounded_uint&;
constexpr auto operator--(int) -> bounded_uint;
};

// Arithmetic operators (throw on out-of-bounds result)
template <auto Min, auto Max>
constexpr auto operator+(bounded_uint<Min, Max> lhs,
bounded_uint<Min, Max> rhs) -> bounded_uint<Min, Max>;

template <auto Min, auto Max>
constexpr auto operator-(bounded_uint<Min, Max> lhs,
bounded_uint<Min, Max> rhs) -> bounded_uint<Min, Max>;

template <auto Min, auto Max>
constexpr auto operator*(bounded_uint<Min, Max> lhs,
bounded_uint<Min, Max> rhs) -> bounded_uint<Min, Max>;

template <auto Min, auto Max>
constexpr auto operator/(bounded_uint<Min, Max> lhs,
bounded_uint<Min, Max> rhs) -> bounded_uint<Min, Max>;

template <auto Min, auto Max>
constexpr auto operator%(bounded_uint<Min, Max> lhs,
bounded_uint<Min, Max> rhs) -> bounded_uint<Min, Max>;

} // namespace boost::safe_numbers
----

== Operator Behavior

=== Construction

[source,c++]
----
explicit constexpr bounded_uint(basis_type val);
----

Constructs a `bounded_uint` from a value of the underlying `basis_type`.
The value is default-initialized to `Min`.
If `val` is less than `Min` or greater than `Max`, `std::domain_error` is thrown.

=== Conversion to Underlying Types

[source,c++]
----
template <typename OtherBasis>
explicit constexpr operator OtherBasis() const;
----

Conversion to other unsigned integral types is explicit.
Widening conversions always succeed.
Narrowing conversions perform a runtime bounds check: if the value exceeds the maximum representable value of the target type, `std::domain_error` is thrown.

=== Conversion to Other Bounded Types

[source,c++]
----
template <auto Min2, auto Max2>
explicit constexpr operator bounded_uint<Min2, Max2>() const;
----

Conversion to a `bounded_uint` with different bounds is explicit.
The target type's constructor validates that the value falls within `[Min2, Max2]`, throwing `std::domain_error` if it does not.

=== Comparison Operators

[source,c++]
----
friend constexpr auto operator<=>(bounded_uint lhs, bounded_uint rhs) noexcept
-> std::strong_ordering = default;
----

Full three-way comparison is supported via `pass:[operator<=>]`, which returns `std::strong_ordering`.
All comparison operators (`<`, `<=`, `>`, `>=`, `==`, `!=`) are available.

=== Arithmetic Operators

[source,c++]
----
template <auto Min, auto Max>
constexpr auto operator+(bounded_uint<Min, Max> lhs,
bounded_uint<Min, Max> rhs) -> bounded_uint<Min, Max>;

template <auto Min, auto Max>
constexpr auto operator-(bounded_uint<Min, Max> lhs,
bounded_uint<Min, Max> rhs) -> bounded_uint<Min, Max>;

template <auto Min, auto Max>
constexpr auto operator*(bounded_uint<Min, Max> lhs,
bounded_uint<Min, Max> rhs) -> bounded_uint<Min, Max>;

template <auto Min, auto Max>
constexpr auto operator/(bounded_uint<Min, Max> lhs,
bounded_uint<Min, Max> rhs) -> bounded_uint<Min, Max>;

template <auto Min, auto Max>
constexpr auto operator%(bounded_uint<Min, Max> lhs,
bounded_uint<Min, Max> rhs) -> bounded_uint<Min, Max>;
----

Arithmetic is performed on the underlying `basis_type` values.
The result is then used to construct a new `bounded_uint`, which validates that it falls within `[Min, Max]`.
If the result is out of bounds, the exception thrown depends on the underlying safe unsigned integer operation:

- `pass:[+]`: Throws `std::overflow_error` if the result exceeds `Max`
- `-`: Throws `std::underflow_error` if the result would be below `Min`
- `pass:[*]`: Throws `std::overflow_error` if the result exceeds `Max`
- `/`: Throws `std::domain_error` if dividing by zero, or `std::domain_error` if the result falls outside `[Min, Max]`
- `%`: Throws `std::domain_error` if the divisor is zero, or `std::domain_error` if the result falls outside `[Min, Max]`

=== Compound Assignment Operators

[source,c++]
----
constexpr auto operator+=(bounded_uint rhs) -> bounded_uint&;
constexpr auto operator-=(bounded_uint rhs) -> bounded_uint&;
constexpr auto operator*=(bounded_uint rhs) -> bounded_uint&;
constexpr auto operator/=(bounded_uint rhs) -> bounded_uint&;
----

Compound assignment operators delegate to their corresponding arithmetic operators and follow the same exception behavior.

=== Increment and Decrement Operators

[source,c++]
----
constexpr auto operator++() -> bounded_uint&;
constexpr auto operator++(int) -> bounded_uint;
constexpr auto operator--() -> bounded_uint&;
constexpr auto operator--(int) -> bounded_uint;
----

- `++` (pre/post): Adds one to the underlying value and validates the result. Throws `std::overflow_error` if the value is already at `Max`, or `std::domain_error` if the incremented value exceeds the upper bound.
- `--` (pre/post): Subtracts one from the underlying value and validates the result. Throws `std::underflow_error` if the value is already at `Min`, or `std::domain_error` if the decremented value falls below the lower bound.

=== Mixed-Bounds Operations

Operations between `bounded_uint` types with different `Min` and `Max` values are compile-time errors.
To perform operations between different bounded types, explicitly convert to the same type first.

== Constexpr Support

All operations are `constexpr`-compatible.
Out-of-bounds results at compile time produce a compiler error.
27 changes: 17 additions & 10 deletions doc/modules/ROOT/pages/charconv.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ https://www.boost.org/LICENSE_1_0.txt

== Description

The library provides character conversion functions for safe integer types using https://www.boost.org/doc/libs/master/libs/charconv/doc/html/charconv.html[Boost.Charconv].
These functions convert between safe integer types and their string representations.
The library provides character conversion functions for all safe integer types using https://www.boost.org/doc/libs/master/libs/charconv/doc/html/charconv.html[Boost.Charconv].
These functions convert between safe integer types (including `bounded_uint`) and their string representations.

[source,c++]
----
Expand All @@ -20,15 +20,15 @@ These functions convert between safe integer types and their string representati
namespace boost::safe_numbers {

// Convert safe integer to character string
template <unsigned_integral BasisType>
template <detail::library_type T>
constexpr auto to_chars(char* first, char* last,
unsigned_integer_basis<BasisType> value,
T value,
int base = 10) -> charconv::to_chars_result;

// Convert character string to safe integer
template <unsigned_integral BasisType>
template <detail::library_type T>
constexpr auto from_chars(const char* first, const char* last,
unsigned_integer_basis<BasisType>& value,
T& value,
int base = 10) -> charconv::from_chars_result;

} // namespace boost::safe_numbers
Expand Down Expand Up @@ -90,13 +90,14 @@ struct from_chars_result

[source,c++]
----
template <unsigned_integral BasisType>
template <detail::library_type T>
constexpr auto to_chars(char* first, char* last,
unsigned_integer_basis<BasisType> value,
T value,
int base = 10) -> charconv::to_chars_result;
----

Converts a safe integer value into a character buffer specified by `[first, last)`.
Works with all safe integer types, including `u8`, `u16`, `u32`, `u64`, `u128`, and `bounded_uint<Min, Max>`.

=== Parameters

Expand All @@ -119,13 +120,14 @@ Returns `boost::charconv::to_chars_result` with:

[source,c++]
----
template <unsigned_integral BasisType>
template <detail::library_type T>
constexpr auto from_chars(const char* first, const char* last,
unsigned_integer_basis<BasisType>& value,
T& value,
int base = 10) -> charconv::from_chars_result;
----

Parses a string from `[first, last)` and converts it into a safe integer value.
Works with all safe integer types, including `u8`, `u16`, `u32`, `u64`, `u128`, and `bounded_uint<Min, Max>`.

=== Parameters

Expand Down Expand Up @@ -162,5 +164,10 @@ to_chars (base 2): 11000000111001
from_chars (base 10): 98765
from_chars (base 16): 6699
from_chars (base 2): 26

bounded to_chars (percent): 75
bounded to_chars (port): 8080
bounded from_chars (percent): 42
bounded from_chars (port): 443
----
====
Loading
Loading