diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b88a048..6b44e6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,8 +38,8 @@ jobs: uses: boostorg/boost-ci/.github/workflows/reusable.yml@master with: exclude_compiler: 'clang-3.5,clang-3.6,clang-3.7,clang-3.8,clang-3.9,clang-4.0,clang-5.0,clang-6.0,clang-7, - clang-8,clang-9,clang-10, - gcc-4.7,gcc-4.8,gcc-4.9,gcc-5,gcc-6,gcc-7,gcc-8' + clang-8,clang-9,clang-10,clang-11,clang-12,clang-13,clang-14,clang-15, + gcc-4.7,gcc-4.8,gcc-4.9,gcc-5,gcc-6,gcc-7,gcc-8,gcc-9,gcc-10' # Example of customization: # with: # enable_reflection: true diff --git a/README.md b/README.md index b7b0a66..08c5b48 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A safe and fully featured replacement for C++ builtin numeric types. This library requires C++20 and is tested on the following platforms: -* GCC 9 and later -* Clang 11 and later +* GCC 11 and later +* Clang 16 and later * Visual Studio 2022 and later * Intel OneAPI DPC++ diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index 51f9925..3a2ad4a 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -14,6 +14,10 @@ ** xref:examples.adoc#examples_fmt_format[Formatting] ** xref:examples.adoc#examples_iostream[Stream I/O] ** xref:examples.adoc#examples_bit[Bit Manipulation] +** xref:examples.adoc#examples_verified_construction[Verified Types: Construction] +** xref:examples.adoc#examples_verified_arithmetic[Verified Types: Arithmetic] +** xref:examples.adoc#examples_verified_runtime[Verified Types: Runtime Capabilities] +** xref:examples.adoc#examples_verified_compile_fail[Verified Types: Compile-Time Overflow] * xref:pretty_printers.adoc[] * xref:api_reference.adoc[] ** xref:api_reference.adoc#api_types[Types] @@ -22,6 +26,7 @@ * xref:policies.adoc[] * xref:unsigned_integers.adoc[] * xref:bounded_uint.adoc[] +* xref:verified_integers.adoc[] * xref:literals.adoc[] * xref:limits.adoc[] * xref:format.adoc[] diff --git a/doc/modules/ROOT/pages/api_reference.adoc b/doc/modules/ROOT/pages/api_reference.adoc index 5d4ed0b..227dd53 100644 --- a/doc/modules/ROOT/pages/api_reference.adoc +++ b/doc/modules/ROOT/pages/api_reference.adoc @@ -43,6 +43,31 @@ https://www.boost.org/LICENSE_1_0.txt | Safe unsigned integer constrained to a compile-time range `[Min, Max]` |=== +=== Verified Integer Types + +[cols="1,2", options="header"] +|=== +| Type | Description + +| xref:verified_integers.adoc[`verified_u8`] +| Compile-time verified safe unsigned 8-bit integer + +| xref:verified_integers.adoc[`verified_u16`] +| Compile-time verified safe unsigned 16-bit integer + +| xref:verified_integers.adoc[`verified_u32`] +| Compile-time verified safe unsigned 32-bit integer + +| xref:verified_integers.adoc[`verified_u64`] +| Compile-time verified safe unsigned 64-bit integer + +| xref:verified_integers.adoc[`verified_u128`] +| Compile-time verified safe unsigned 128-bit integer + +| xref:verified_integers.adoc[`verified_bounded_integer`] +| Compile-time verified bounded unsigned integer constrained to `[Min, Max]` +|=== + === Enumerations [cols="1,2", options="header"] @@ -197,4 +222,7 @@ This header is not included in the convenience header since it requires external | `` | Bounded unsigned integer type (`bounded_uint`) + +| `` +| Verified integer types (`verified_u8`, `verified_u16`, `verified_u32`, `verified_u64`, `verified_u128`, `verified_bounded_integer`) |=== diff --git a/doc/modules/ROOT/pages/bit.adoc b/doc/modules/ROOT/pages/bit.adoc index 95fc696..43986ca 100644 --- a/doc/modules/ROOT/pages/bit.adoc +++ b/doc/modules/ROOT/pages/bit.adoc @@ -169,6 +169,31 @@ 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. +== Verified Types + +The bit functions have special behavior with xref:verified_integers.adoc[verified types]: + +Functions that return `int` or `bool` (`has_single_bit`, `bit_width`, `countl_zero`, `countl_one`, `countr_zero`, `countr_one`, `popcount`) work at runtime with verified types. +These functions only read the value via the `constexpr` conversion operator. + +Functions that return the safe type (`bit_ceil`, `bit_floor`, `rotl`, `rotr`, `byteswap`) have `consteval` overloads for verified types, meaning they can only be called at compile time. +The `consteval` overloads use a `requires` clause to distinguish them: + +[source,c++] +---- +// Runtime overload (non-verified types) +template + requires (!is_verified_type_v) +constexpr auto bit_ceil(UnsignedInt x) noexcept -> UnsignedInt; + +// Compile-time overload (verified types only) +template + requires is_verified_type_v +consteval auto bit_ceil(UnsignedInt x) noexcept -> UnsignedInt; +---- + +The same pattern applies to `bit_floor`, `rotl`, `rotr`, and `byteswap`. + == Examples .This https://github.com/boostorg/safe_numbers/blob/develop/examples/bit.cpp[example] demonstrates the bit manipulation functions. diff --git a/doc/modules/ROOT/pages/charconv.adoc b/doc/modules/ROOT/pages/charconv.adoc index 1ae0b06..187faff 100644 --- a/doc/modules/ROOT/pages/charconv.adoc +++ b/doc/modules/ROOT/pages/charconv.adoc @@ -158,6 +158,28 @@ Returns `boost::charconv::from_chars_result` with: - `ptr` pointing to the first character not matching the pattern - `ec` set to `std::errc{}` on success, `std::errc::invalid_argument` if no valid conversion, or `std::errc::result_out_of_range` if the value overflows +== Verified Types + +`to_chars` works at runtime with xref:verified_integers.adoc[verified types] since it only reads the value. + +`from_chars` has a `consteval` overload for verified types -- it can only be used at compile time. +The two overloads are distinguished via a `requires` clause: + +[source,c++] +---- +// Runtime overload (non-verified types) +template + requires (!detail::is_verified_type_v) +constexpr auto from_chars(const char* first, const char* last, + T& value, int base = 10) -> charconv::from_chars_result; + +// Compile-time overload (verified types only) +template + requires detail::is_verified_type_v +consteval auto from_chars(const char* first, const char* last, + T& value, int base = 10) -> charconv::from_chars_result; +---- + == Examples .This https://github.com/boostorg/safe_numbers/blob/develop/examples/charconv.cpp[example] demonstrates how to use `to_chars` and `from_chars` with safe integer types. diff --git a/doc/modules/ROOT/pages/examples.adoc b/doc/modules/ROOT/pages/examples.adoc index 5f892c3..26fbb82 100644 --- a/doc/modules/ROOT/pages/examples.adoc +++ b/doc/modules/ROOT/pages/examples.adoc @@ -414,6 +414,117 @@ shr(u32(8), 1) = 4 ---- ==== +[#examples_verified_construction] +== Verified Types: Construction and Runtime Usage + +Verified types use `consteval` constructors and arithmetic, guaranteeing that all values are validated at compile time. +At runtime, verified types are read-only constants that support output streaming, explicit conversions, and comparisons. + +.This https://github.com/boostorg/safe_numbers/blob/develop/examples/verified_construction.cpp[example] demonstrates construction and runtime use of verified types. +==== +[source, c++] +---- +include::example$verified_construction.cpp[] +---- + +Output: +---- +verified_u8: 42 +verified_u16: 1000 +verified_u32: 100000 +verified_u64: 9999999999 +verified_u32: 42 + +Converted to uint32_t: 100000 +42 < 50000: true +42 == 42: true + +percent: 50 +---- +==== + +[#examples_verified_arithmetic] +== Verified Types: Compile-Time Arithmetic + +All arithmetic on verified types (`+`, `-`, `*`, `/`, `%`, `+=`, `++`, etc.) is `consteval` -- evaluated entirely by the compiler. +If an operation overflows, it produces a compile error instead of a runtime exception. + +.This https://github.com/boostorg/safe_numbers/blob/develop/examples/verified_arithmetic.cpp[example] demonstrates compile-time arithmetic with verified types. +==== +[source, c++] +---- +include::example$verified_arithmetic.cpp[] +---- + +Output: +---- +100 + 200 = 300 +200 - 100 = 100 +15 * 20 = 300 +300 / 10 = 30 +17 % 5 = 2 + +10 after ++: 11 +0 + 100 + 200 + 300 = 600 + +bounded 500 + 400 = 900 +---- +==== + +[#examples_verified_runtime] +== Verified Types: Runtime Capabilities + +While construction and arithmetic are compile-time only, verified types support a wide range of runtime operations: +output streaming, explicit conversions, comparisons, `to_chars`, bit queries, and `std::numeric_limits`. + +.This https://github.com/boostorg/safe_numbers/blob/develop/examples/verified_runtime_usage.cpp[example] shows what verified types can do at runtime. +==== +[source, c++] +---- +include::example$verified_runtime_usage.cpp[] +---- + +Output: +---- +Value: 1024 +As uint32_t: 1024 +As u32: 1024 + +10 == 10: true +10 != 50000: true +10 < 50000: true +10 >= 50000: false + +to_chars (base 10): 1024 +to_chars (base 16): 400 + +has_single_bit(1024) = true +bit_width(1024) = 11 +countl_zero(1024) = 21 +popcount(1024) = 1 +bit_ceil(48) = 64 +bit_floor(48) = 32 + +numeric_limits::min() = 0 +numeric_limits::max() = 255 +numeric_limits::digits = 32 +---- +==== + +[#examples_verified_compile_fail] +== Verified Types: Compile-Time Overflow Detection + +Verified types catch overflow at compile time. +Attempting to overflow a verified type produces a compiler error rather than a runtime exception. + +.This https://github.com/boostorg/safe_numbers/blob/develop/examples/compile_fail_verified_overflow.cpp[example] fails to compile because `u8(255) + u8(1)` overflows. +==== +[source, c++] +---- +include::example$compile_fail_verified_overflow.cpp[] +---- +==== + [#examples_policy_comparison] == Policy Comparison diff --git a/doc/modules/ROOT/pages/format.adoc b/doc/modules/ROOT/pages/format.adoc index 1b8253f..c44703b 100644 --- a/doc/modules/ROOT/pages/format.adoc +++ b/doc/modules/ROOT/pages/format.adoc @@ -124,6 +124,11 @@ The full format specifier order is: {[fill][align][sign][#][0][width][.precision][L][type]} ---- +== Verified Types + +Formatting works with xref:verified_integers.adoc[verified types] through the `library_type` concept. +Both `std::format` and `pass:[{fmt}]` work at runtime via the `constexpr` conversion operator, since the formatter only reads the value. + == Examples IMPORTANT: The header `` is *NOT* part of the convenience header, because it is an optional dependency on a potentially compiled library. diff --git a/doc/modules/ROOT/pages/iostream.adoc b/doc/modules/ROOT/pages/iostream.adoc index 0a566a8..9a62a0f 100644 --- a/doc/modules/ROOT/pages/iostream.adoc +++ b/doc/modules/ROOT/pages/iostream.adoc @@ -72,6 +72,23 @@ Standard stream manipulators (`std::hex`, `std::oct`, `std::dec`, `std::setw`, e For `u8`, the input is read as a `std::uint32_t` to avoid character interpretation, then narrowed to `std::uint8_t`. +== Verified Types + +`operator>>` is not available for xref:verified_integers.adoc[verified types]. +The overload is excluded via a `requires` clause: + +[source,c++] +---- +template + requires (!is_verified_type_v) +auto operator>>(std::basic_istream& is, LibType& v) + -> std::basic_istream&; +---- + +This is by design: verified types can only be constructed at compile time, so reading a value from a stream into a verified type is not meaningful. + +`operator<<` works normally with verified types via their `constexpr` conversion operator, since output only reads the value. + == Examples .This https://github.com/boostorg/safe_numbers/blob/develop/examples/iostream.cpp[example] demonstrates stream I/O with safe integer types. diff --git a/doc/modules/ROOT/pages/limits.adoc b/doc/modules/ROOT/pages/limits.adoc index fe9aa3a..ff9312e 100644 --- a/doc/modules/ROOT/pages/limits.adoc +++ b/doc/modules/ROOT/pages/limits.adoc @@ -239,6 +239,37 @@ class numeric_limits> NOTE: The non-applicable member functions (`epsilon`, `round_error`, `infinity`, `quiet_NaN`, `signaling_NaN`, `denorm_min`) return `min()` rather than a zero-constructed value, since zero may fall outside the valid range for types with a non-zero lower bound. +== Verified Integer Specialization + +A partial specialization of `std::numeric_limits` is provided for all `verified_type_basis` types, including both `verified_u8`-`verified_u128` and `verified_bounded_integer`. + +[source,c++] +---- +namespace std { + +template +class numeric_limits> +{ + using type = boost::safe_numbers::detail::verified_type_basis; + using basis_limits = std::numeric_limits; + + // Static member constants delegate to std::numeric_limits + // ... + + static constexpr type min(); + static constexpr type max(); + static constexpr type lowest(); + // ... remaining functions delegate to basis_limits +}; + +} // namespace std +---- + +Static properties and functions delegate to `std::numeric_limits` (the safe integer basis type, not the underlying fundamental type). +This means verified bounded types correctly report their bounded `min()` and `max()` values. + +The `static constexpr` member functions work because constructing the verified type with a constant expression invokes the `consteval` constructor as an immediate invocation, which is permitted in a `constexpr` context. + === Example .This https://github.com/boostorg/safe_numbers/blob/develop/examples/bounded_limits.cpp[example] demonstrates `std::numeric_limits` with bounded integer types. diff --git a/doc/modules/ROOT/pages/overview.adoc b/doc/modules/ROOT/pages/overview.adoc index b907daa..fdc7bdd 100644 --- a/doc/modules/ROOT/pages/overview.adoc +++ b/doc/modules/ROOT/pages/overview.adoc @@ -30,8 +30,8 @@ Safety critical applications Boost.safe_numbers is tested natively on Ubuntu (x86_64, x86_32, s390x, aarch64, ARM32v7), macOS (x86_64, and Apple Silicon), and Windows (x86_64, x86_32, and ARM64); as well as emulated PPC64LE using QEMU with the following compilers: -* GCC 9 and later -* Clang 11 and later +* GCC 11 and later +* Clang 16 and later * Visual Studio 2022 (14.3) and later * Intel OneAPI DPC++ 2024.2 and later diff --git a/doc/modules/ROOT/pages/verified_integers.adoc b/doc/modules/ROOT/pages/verified_integers.adoc new file mode 100644 index 0000000..296a2ef --- /dev/null +++ b/doc/modules/ROOT/pages/verified_integers.adoc @@ -0,0 +1,216 @@ +//// +Copyright 2026 Matt Borland +Distributed under the Boost Software License, Version 1.0. +https://www.boost.org/LICENSE_1_0.txt +//// + +[#verified_integers] + += 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` | `bounded_uint` | smallest unsigned type fitting `Max` +|=== + +Each type exposes an `underlying_type` member type alias that refers to the underlying fundamental integer type. + +[source,c++] +---- +#include + +namespace boost::safe_numbers { + +using verified_u8 = detail::verified_type_basis; +using verified_u16 = detail::verified_type_basis; +using verified_u32 = detail::verified_type_basis; +using verified_u64 = detail::verified_type_basis; +using verified_u128 = detail::verified_type_basis; + +template +using verified_bounded_integer = detail::verified_type_basis>; + +template +class verified_type_basis { + +public: + using underlying_type = underlying_type_t; + + // 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 +consteval auto operator+(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis; + +template +consteval auto operator-(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis; + +template +consteval auto operator*(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis; + +template +consteval auto operator/(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis; + +template +consteval auto operator%(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis; + +} // namespace boost::safe_numbers +---- + +== Operator Behavior + +=== Construction + +[source,c++] +---- +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 + +[source,c++] +---- +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 + +[source,c++] +---- +friend constexpr auto operator<=>(verified_type_basis lhs, verified_type_basis 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. +Comparisons are `constexpr` and work at runtime. + +=== Arithmetic Operators + +[source,c++] +---- +template +consteval auto operator+(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis; + +template +consteval auto operator-(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis; + +template +consteval auto operator*(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis; + +template +consteval auto operator/(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis; + +template +consteval auto operator%(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis; +---- + +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 + +[source,c++] +---- +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 + +[source,c++] +---- +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 +|=== + +== Integration with Other Features + +Verified types integrate with the library's other features: + +- **xref:bit.adoc[]**: 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. +- **xref:charconv.adoc[]**: `to_chars` works at runtime (only reads the value). `from_chars` has a `consteval` overload for verified types. +- **xref:iostream.adoc[]**: `operator<<` works normally at runtime. `operator>>` is excluded for verified types since they cannot be constructed at runtime. +- **xref:limits.adoc[]**: `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. +- **xref:format.adoc[]**: Both `std::format` and `pass:[{fmt}]` work at runtime via the `constexpr` conversion operator. diff --git a/examples/compile_fail_verified_overflow.cpp b/examples/compile_fail_verified_overflow.cpp new file mode 100644 index 0000000..996876b --- /dev/null +++ b/examples/compile_fail_verified_overflow.cpp @@ -0,0 +1,21 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// This example demonstrates that verified types catch overflow at compile time. +// This file is expected to fail compilation. + +#include + +int main() +{ + using namespace boost::safe_numbers; + + // u8 can hold 0-255. Adding 1 to 255 overflows. + // With a verified type this is a compile error, not a runtime exception. + constexpr auto result {verified_u8{u8{255}} + verified_u8{u8{1}}}; + + static_cast(result); + + return 0; +} diff --git a/examples/verified_arithmetic.cpp b/examples/verified_arithmetic.cpp new file mode 100644 index 0000000..ea8fa9b --- /dev/null +++ b/examples/verified_arithmetic.cpp @@ -0,0 +1,72 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// Verified types enforce that all arithmetic is evaluated at compile time. +// If an operation would overflow, it produces a compile error instead of +// a runtime exception. + +#include +#include +#include + +int main() +{ + using namespace boost::safe_numbers; + + // All arithmetic is consteval - evaluated entirely by the compiler. + // The results are compile-time constants that cannot be modified at runtime. + + // Basic arithmetic + constexpr auto sum {verified_u32{100U} + verified_u32{200U}}; + constexpr auto diff {verified_u32{200U} - verified_u32{100U}}; + constexpr auto prod {verified_u32{15U} * verified_u32{20U}}; + constexpr auto quot {verified_u32{300U} / verified_u32{10U}}; + constexpr auto rem {verified_u32{17U} % verified_u32{5U}}; + + std::cout << "100 + 200 = " << sum << '\n'; + std::cout << "200 - 100 = " << diff << '\n'; + std::cout << "15 * 20 = " << prod << '\n'; + std::cout << "300 / 10 = " << quot << '\n'; + std::cout << "17 % 5 = " << rem << '\n'; + + std::cout << '\n'; + + // Compound assignment and increment/decrement are also consteval. + // They work inside consteval contexts like consteval functions: + constexpr auto incremented = []() consteval { + auto val = verified_u8{u8{10}}; + ++val; + return val; + }(); + std::cout << "10 after ++: " << incremented << '\n'; + + constexpr auto accumulated = []() consteval { + auto val = verified_u16{u16{0}}; + val += verified_u16{u16{100}}; + val += verified_u16{u16{200}}; + val += verified_u16{u16{300}}; + return val; + }(); + std::cout << "0 + 100 + 200 + 300 = " << accumulated << '\n'; + + std::cout << '\n'; + + // Overflow is caught at compile time. + // These would produce compile errors: + // + // constexpr auto overflow {verified_u8{u8{255}} + verified_u8{u8{1}}}; + // error: consteval call to overflow-checking addition fails + // + // constexpr auto underflow {verified_u8{u8{0}} - verified_u8{u8{1}}}; + // error: consteval call to overflow-checking subtraction fails + + // Bounded arithmetic is also compile-time checked + constexpr auto bounded_sum { + verified_bounded_integer<0u, 1000u>{500u} + + verified_bounded_integer<0u, 1000u>{400u} + }; + std::cout << "bounded 500 + 400 = " << bounded_sum << '\n'; + + return 0; +} diff --git a/examples/verified_construction.cpp b/examples/verified_construction.cpp new file mode 100644 index 0000000..ed30d3f --- /dev/null +++ b/examples/verified_construction.cpp @@ -0,0 +1,66 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// Verified types guarantee that all values are validated at compile time. +// Construction and arithmetic are consteval (compile-time only), +// while conversions and comparisons are constexpr (usable at runtime). + +#include +#include +#include +#include + +int main() +{ + using namespace boost::safe_numbers; + + // Construction must happen at compile time. + // All of these are evaluated by the compiler: + constexpr verified_u8 a {u8{42}}; + constexpr verified_u16 b {u16{1000}}; + constexpr verified_u32 c {u32{100000}}; + constexpr verified_u64 d {u64{9999999999ULL}}; + + // You can also construct from the underlying fundamental type directly + constexpr verified_u32 e {42U}; + + // At runtime, verified types are read-only values. + // Output works because operator<< uses the constexpr conversion operator: + std::cout << "verified_u8: " << a << '\n'; + std::cout << "verified_u16: " << b << '\n'; + std::cout << "verified_u32: " << c << '\n'; + std::cout << "verified_u64: " << d << '\n'; + std::cout << "verified_u32: " << e << '\n'; + + std::cout << '\n'; + + // Explicit conversion back to fundamental types works at runtime + const auto raw = static_cast(c); + std::cout << "Converted to uint32_t: " << raw << '\n'; + + // Comparisons work at runtime (same-type only) + constexpr verified_u32 f {u32{50000}}; + if (e < f) + { + std::cout << "42 < 50000: true\n"; + } + + // The three-way comparison operator is also available + if ((e <=> e) == std::strong_ordering::equal) + { + std::cout << "42 == 42: true\n"; + } + + std::cout << '\n'; + + // Bounded verified types work the same way. + // The bounded range is enforced at compile time: + constexpr verified_bounded_integer<0u, 100u> percent {50u}; + std::cout << "percent: " << percent << '\n'; + + // This would be a compile error - value out of bounds: + // constexpr verified_bounded_integer<0u, 100u> bad {101u}; + + return 0; +} diff --git a/examples/verified_runtime_usage.cpp b/examples/verified_runtime_usage.cpp new file mode 100644 index 0000000..d3b7262 --- /dev/null +++ b/examples/verified_runtime_usage.cpp @@ -0,0 +1,106 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// This example shows what you can do with verified types at runtime. +// While construction and arithmetic are compile-time only, verified types +// are designed to be useful runtime constants: they support output streaming, +// to_chars, std::format/fmt::format, comparisons, and bit queries. + +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + using namespace boost::safe_numbers; + + constexpr verified_u32 val {1024U}; + + // --- Output streaming (runtime) --- + std::cout << "Value: " << val << '\n'; + + // --- Conversions (runtime) --- + // Explicit cast back to the fundamental type + const auto raw = static_cast(val); + std::cout << "As uint32_t: " << raw << '\n'; + + // Explicit cast to the safe basis type + const auto basis = static_cast(val); + std::cout << "As u32: " << basis << '\n'; + + std::cout << '\n'; + + // --- Comparisons (runtime) --- + constexpr verified_u32 small {10U}; + constexpr verified_u32 large {50000U}; + + std::cout << std::boolalpha; + std::cout << "10 == 10: " << (small == small) << '\n'; + std::cout << "10 != 50000: " << (small != large) << '\n'; + std::cout << "10 < 50000: " << (small < large) << '\n'; + std::cout << "10 >= 50000: " << (small >= large) << '\n'; + + std::cout << '\n'; + + // --- to_chars (runtime) --- + // Writing to a character buffer works at runtime since it only + // reads the value through the constexpr conversion operator + char buffer[32]; + auto result = to_chars(buffer, buffer + sizeof(buffer), val); + if (result) + { + *result.ptr = '\0'; + std::cout << "to_chars (base 10): " << buffer << '\n'; + } + + result = to_chars(buffer, buffer + sizeof(buffer), val, 16); + if (result) + { + *result.ptr = '\0'; + std::cout << "to_chars (base 16): " << buffer << '\n'; + } + + std::cout << '\n'; + + // --- Bit queries (runtime) --- + // Functions returning int or bool work at runtime + std::cout << "has_single_bit(1024) = " << has_single_bit(val) << '\n'; + std::cout << "bit_width(1024) = " << bit_width(val) << '\n'; + std::cout << "countl_zero(1024) = " << countl_zero(val) << '\n'; + std::cout << "popcount(1024) = " << popcount(val) << '\n'; + + // Functions returning the verified type are consteval. + // They work as compile-time constants: + constexpr auto ceiled = bit_ceil(verified_u32{48U}); + constexpr auto floored = bit_floor(verified_u32{48U}); + std::cout << "bit_ceil(48) = " << ceiled << '\n'; + std::cout << "bit_floor(48) = " << floored << '\n'; + + std::cout << '\n'; + + // --- std::numeric_limits (runtime) --- + std::cout << "numeric_limits::min() = " + << std::numeric_limits::min() << '\n'; + std::cout << "numeric_limits::max() = " + << std::numeric_limits::max() << '\n'; + std::cout << "numeric_limits::digits = " + << std::numeric_limits::digits << '\n'; + + // --- What does NOT work at runtime --- + // The following operations require compile-time evaluation: + // + // verified_u32 x {some_runtime_variable}; // ERROR: consteval constructor + // auto sum = a + b; // Only at compile time, e.g. constexpr auto sum = a + b; + // from_chars(str, str + len, verified_val); // consteval only + // bit_ceil(verified_val); // consteval only (returns verified type) + // + // This is by design: verified types are compile-time validated constants + // that can be safely passed around and inspected at runtime. + + return 0; +} diff --git a/include/boost/safe_numbers.hpp b/include/boost/safe_numbers.hpp index d497702..481b8d7 100644 --- a/include/boost/safe_numbers.hpp +++ b/include/boost/safe_numbers.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff --git a/include/boost/safe_numbers/bit.hpp b/include/boost/safe_numbers/bit.hpp index e456c56..6dba2dc 100644 --- a/include/boost/safe_numbers/bit.hpp +++ b/include/boost/safe_numbers/bit.hpp @@ -18,7 +18,7 @@ namespace boost::safe_numbers { BOOST_SAFE_NUMBERS_EXPORT template -constexpr auto has_single_bit(const UnsignedInt x) noexcept -> bool +[[nodiscard]] constexpr auto has_single_bit(const UnsignedInt x) noexcept -> bool { using boost::core::has_single_bit; using underlying_type = detail::underlying_type_t; @@ -27,7 +27,8 @@ constexpr auto has_single_bit(const UnsignedInt x) noexcept -> bool } BOOST_SAFE_NUMBERS_EXPORT template -constexpr auto bit_ceil(const UnsignedInt x) noexcept -> UnsignedInt + requires (!detail::is_verified_type_v) +[[nodiscard]] constexpr auto bit_ceil(const UnsignedInt x) noexcept -> UnsignedInt { using boost::core::bit_ceil; using underlying_type = detail::underlying_type_t; @@ -36,7 +37,28 @@ constexpr auto bit_ceil(const UnsignedInt x) noexcept -> UnsignedInt } BOOST_SAFE_NUMBERS_EXPORT template -constexpr auto bit_floor(const UnsignedInt x) noexcept -> UnsignedInt + requires detail::is_verified_type_v +[[nodiscard]] consteval auto bit_ceil(const UnsignedInt x) noexcept -> UnsignedInt +{ + using boost::core::bit_ceil; + using underlying_type = detail::underlying_type_t; + + return UnsignedInt{bit_ceil(static_cast(x))}; +} + +BOOST_SAFE_NUMBERS_EXPORT template + requires (!detail::is_verified_type_v) +[[nodiscard]] constexpr auto bit_floor(const UnsignedInt x) noexcept -> UnsignedInt +{ + using boost::core::bit_floor; + using underlying_type = detail::underlying_type_t; + + return UnsignedInt{bit_floor(static_cast(x))}; +} + +BOOST_SAFE_NUMBERS_EXPORT template + requires detail::is_verified_type_v +[[nodiscard]] consteval auto bit_floor(const UnsignedInt x) noexcept -> UnsignedInt { using boost::core::bit_floor; using underlying_type = detail::underlying_type_t; @@ -45,7 +67,7 @@ constexpr auto bit_floor(const UnsignedInt x) noexcept -> UnsignedInt } BOOST_SAFE_NUMBERS_EXPORT template -constexpr auto bit_width(const UnsignedInt x) noexcept -> int +[[nodiscard]] constexpr auto bit_width(const UnsignedInt x) noexcept -> int { using boost::core::bit_width; using underlying_type = detail::underlying_type_t; @@ -54,7 +76,18 @@ constexpr auto bit_width(const UnsignedInt x) noexcept -> int } BOOST_SAFE_NUMBERS_EXPORT template -constexpr auto rotl(const UnsignedInt x, const int s) noexcept -> UnsignedInt + requires (!detail::is_verified_type_v) +[[nodiscard]] constexpr auto rotl(const UnsignedInt x, const int s) noexcept -> UnsignedInt +{ + using boost::core::rotl; + using underlying_type = detail::underlying_type_t; + + return UnsignedInt{rotl(static_cast(x), s)}; +} + +BOOST_SAFE_NUMBERS_EXPORT template + requires detail::is_verified_type_v +[[nodiscard]] consteval auto rotl(const UnsignedInt x, const int s) noexcept -> UnsignedInt { using boost::core::rotl; using underlying_type = detail::underlying_type_t; @@ -63,7 +96,18 @@ constexpr auto rotl(const UnsignedInt x, const int s) noexcept -> UnsignedInt } BOOST_SAFE_NUMBERS_EXPORT template -constexpr auto rotr(const UnsignedInt x, const int s) noexcept -> UnsignedInt + requires (!detail::is_verified_type_v) +[[nodiscard]] constexpr auto rotr(const UnsignedInt x, const int s) noexcept -> UnsignedInt +{ + using boost::core::rotr; + using underlying_type = detail::underlying_type_t; + + return UnsignedInt{rotr(static_cast(x), s)}; +} + +BOOST_SAFE_NUMBERS_EXPORT template + requires detail::is_verified_type_v +[[nodiscard]] consteval auto rotr(const UnsignedInt x, const int s) noexcept -> UnsignedInt { using boost::core::rotr; using underlying_type = detail::underlying_type_t; @@ -72,7 +116,7 @@ constexpr auto rotr(const UnsignedInt x, const int s) noexcept -> UnsignedInt } BOOST_SAFE_NUMBERS_EXPORT template -constexpr auto countl_zero(const UnsignedInt x) noexcept -> int +[[nodiscard]] constexpr auto countl_zero(const UnsignedInt x) noexcept -> int { using boost::core::countl_zero; using underlying_type = detail::underlying_type_t; @@ -81,7 +125,7 @@ constexpr auto countl_zero(const UnsignedInt x) noexcept -> int } BOOST_SAFE_NUMBERS_EXPORT template -constexpr auto countl_one(const UnsignedInt x) noexcept -> int +[[nodiscard]] constexpr auto countl_one(const UnsignedInt x) noexcept -> int { using boost::core::countl_one; using underlying_type = detail::underlying_type_t; @@ -90,7 +134,7 @@ constexpr auto countl_one(const UnsignedInt x) noexcept -> int } BOOST_SAFE_NUMBERS_EXPORT template -constexpr auto countr_zero(const UnsignedInt x) noexcept -> int +[[nodiscard]] constexpr auto countr_zero(const UnsignedInt x) noexcept -> int { using boost::core::countr_zero; using underlying_type = detail::underlying_type_t; @@ -99,7 +143,7 @@ constexpr auto countr_zero(const UnsignedInt x) noexcept -> int } BOOST_SAFE_NUMBERS_EXPORT template -constexpr auto countr_one(const UnsignedInt x) noexcept -> int +[[nodiscard]] constexpr auto countr_one(const UnsignedInt x) noexcept -> int { using boost::core::countr_one; using underlying_type = detail::underlying_type_t; @@ -108,7 +152,7 @@ constexpr auto countr_one(const UnsignedInt x) noexcept -> int } BOOST_SAFE_NUMBERS_EXPORT template -constexpr auto popcount(const UnsignedInt x) noexcept -> int +[[nodiscard]] constexpr auto popcount(const UnsignedInt x) noexcept -> int { using boost::core::popcount; using underlying_type = detail::underlying_type_t; @@ -117,7 +161,18 @@ constexpr auto popcount(const UnsignedInt x) noexcept -> int } BOOST_SAFE_NUMBERS_EXPORT template -constexpr auto byteswap(const UnsignedInt x) noexcept -> UnsignedInt + requires (!detail::is_verified_type_v) +[[nodiscard]] constexpr auto byteswap(const UnsignedInt x) noexcept -> UnsignedInt +{ + using boost::core::byteswap; + using underlying_type = detail::underlying_type_t; + + return UnsignedInt{byteswap(static_cast(x))}; +} + +BOOST_SAFE_NUMBERS_EXPORT template + requires detail::is_verified_type_v +[[nodiscard]] consteval auto byteswap(const UnsignedInt x) noexcept -> UnsignedInt { using boost::core::byteswap; using underlying_type = detail::underlying_type_t; diff --git a/include/boost/safe_numbers/bounded_integers.hpp b/include/boost/safe_numbers/bounded_integers.hpp index f13442b..64f784a 100644 --- a/include/boost/safe_numbers/bounded_integers.hpp +++ b/include/boost/safe_numbers/bounded_integers.hpp @@ -260,6 +260,8 @@ constexpr auto OP_SYMBOL(const boost::safe_numbers::bounded_uint namespace boost::safe_numbers { +BOOST_SAFE_NUMBERS_DEFINE_MIXED_BOUNDED_UINT_OP("comparison", operator<=>) +BOOST_SAFE_NUMBERS_DEFINE_MIXED_BOUNDED_UINT_OP("equality", operator==) BOOST_SAFE_NUMBERS_DEFINE_MIXED_BOUNDED_UINT_OP("addition", operator+) BOOST_SAFE_NUMBERS_DEFINE_MIXED_BOUNDED_UINT_OP("subtraction", operator-) BOOST_SAFE_NUMBERS_DEFINE_MIXED_BOUNDED_UINT_OP("multiplication", operator*) diff --git a/include/boost/safe_numbers/charconv.hpp b/include/boost/safe_numbers/charconv.hpp index 2103152..91c9fd3 100644 --- a/include/boost/safe_numbers/charconv.hpp +++ b/include/boost/safe_numbers/charconv.hpp @@ -17,6 +17,7 @@ namespace boost::safe_numbers { template + requires (!detail::is_verified_type_v) constexpr auto from_chars(const char* first, const char* last, T& value, int base = 10) -> charconv::from_chars_result { @@ -29,6 +30,20 @@ constexpr auto from_chars(const char* first, const char* last, T& value, int bas return r; } +template + requires detail::is_verified_type_v +consteval auto from_chars(const char* first, const char* last, T& value, int base = 10) + -> charconv::from_chars_result +{ + using underlying_type = detail::underlying_type_t; + + underlying_type result {}; + const auto r {charconv::from_chars(first, last, result, base)}; + value = T{result}; + + return r; +} + template constexpr auto to_chars(char* first, char* last, const T value, int base = 10) -> charconv::to_chars_result diff --git a/include/boost/safe_numbers/detail/type_traits.hpp b/include/boost/safe_numbers/detail/type_traits.hpp index 36111ca..91f441d 100644 --- a/include/boost/safe_numbers/detail/type_traits.hpp +++ b/include/boost/safe_numbers/detail/type_traits.hpp @@ -149,6 +149,39 @@ concept unsigned_library_type = is_unsigned_library_type_v; template concept non_bounded_unsigned_library_type = is_unsigned_library_type_v && !is_bounded_type_v; +// Forward declaration of verified_type_basis +template +class verified_type_basis; + +// is_verified_type trait + +namespace impl { + +template +struct is_verified_type : std::false_type {}; + +template +struct is_verified_type> : std::true_type {}; + +template +struct is_library_type> : std::true_type {}; + +template + requires is_unsigned_library_type_v +struct is_unsigned_library_type> : std::true_type {}; + +template + requires is_bounded_type_v +struct is_bounded_type> : std::true_type {}; + +} // namespace impl + +template +inline constexpr bool is_verified_type_v = impl::is_verified_type::value; + +template +concept verified_type = is_verified_type_v; + // underlying specialization for bounded_uint namespace impl { @@ -159,6 +192,12 @@ struct underlying> using type = typename underlying::basis_type>::type; }; +template +struct underlying> +{ + using type = typename underlying::type; +}; + } // namespace impl // Promotes an unsigned integer to the next higher type diff --git a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp index c1ca4b3..06c21d8 100644 --- a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp +++ b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp @@ -489,6 +489,9 @@ constexpr auto OP_SYMBOL(const boost::safe_numbers::detail::unsigned_integer_bas namespace boost::safe_numbers::detail { +BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("comparison", operator<=>) +BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("equality", operator==) + BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("addition", operator+) template diff --git a/include/boost/safe_numbers/detail/verified_type_basis.hpp b/include/boost/safe_numbers/detail/verified_type_basis.hpp new file mode 100644 index 0000000..c951f10 --- /dev/null +++ b/include/boost/safe_numbers/detail/verified_type_basis.hpp @@ -0,0 +1,217 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_SAFE_NUMBERS_VERIFIED_INTEGER_BASIS_HPP +#define BOOST_SAFE_NUMBERS_VERIFIED_INTEGER_BASIS_HPP + +#include +#include +#include +#include + +#ifndef BOOST_SAFE_NUMBERS_BUILD_MODULE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // BOOST_SAFE_NUMBERS_BUILD_MODULE + +namespace boost::safe_numbers::detail { + +template +class verified_type_basis +{ +private: + + using underlying_type = underlying_type_t; + + BasisType basis_ {}; + +public: + + explicit consteval verified_type_basis(const BasisType basis) : basis_{basis} {} + + explicit consteval verified_type_basis(const underlying_type val) : verified_type_basis{BasisType{val}} {} + + [[nodiscard]] explicit constexpr operator BasisType() const noexcept { return basis_; } + + [[nodiscard]] explicit constexpr operator underlying_type() const noexcept { return static_cast(basis_); } + + [[nodiscard]] friend constexpr auto operator<=>(verified_type_basis lhs, verified_type_basis rhs) noexcept + -> std::strong_ordering = default; + + 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&; + + 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; +}; + +template +[[nodiscard]] consteval auto operator+(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis +{ + return verified_type_basis{static_cast(lhs) + static_cast(rhs)}; +} + +template +[[nodiscard]] consteval auto operator-(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis +{ + return verified_type_basis{static_cast(lhs) - static_cast(rhs)}; +} + +template +[[nodiscard]] consteval auto operator*(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis +{ + return verified_type_basis{static_cast(lhs) * static_cast(rhs)}; +} + +template +[[nodiscard]] consteval auto operator/(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis +{ + return verified_type_basis{static_cast(lhs) / static_cast(rhs)}; +} + +template +[[nodiscard]] consteval auto operator%(const verified_type_basis lhs, + const verified_type_basis rhs) -> verified_type_basis +{ + return verified_type_basis{static_cast(lhs) % static_cast(rhs)}; +} + +template +consteval auto verified_type_basis::operator+=(verified_type_basis rhs) -> verified_type_basis& +{ + *this = *this + rhs; + return *this; +} + +template +consteval auto verified_type_basis::operator-=(verified_type_basis rhs) -> verified_type_basis& +{ + *this = *this - rhs; + return *this; +} + +template +consteval auto verified_type_basis::operator*=(verified_type_basis rhs) -> verified_type_basis& +{ + *this = *this * rhs; + return *this; +} + +template +consteval auto verified_type_basis::operator/=(verified_type_basis rhs) -> verified_type_basis& +{ + *this = *this / rhs; + return *this; +} + +template +consteval auto verified_type_basis::operator%=(verified_type_basis rhs) -> verified_type_basis& +{ + *this = *this % rhs; + return *this; +} + +template +consteval auto verified_type_basis::operator++() -> verified_type_basis& +{ + ++basis_; + return *this; +} + +template +consteval auto verified_type_basis::operator++(int) -> verified_type_basis +{ + auto tmp = *this; + ++(*this); + return tmp; +} + +template +consteval auto verified_type_basis::operator--() -> verified_type_basis& +{ + --basis_; + return *this; +} + +template +consteval auto verified_type_basis::operator--(int) -> verified_type_basis +{ + auto tmp = *this; + --(*this); + return tmp; +} + +// Mixed-type operation guards: produce clear static_assert messages +// instead of cryptic "invalid operands to binary expression" errors. + +#define BOOST_SAFE_NUMBERS_DEFINE_MIXED_VERIFIED_OP(OP_NAME, OP_SYMBOL) \ +template \ + requires (!std::is_same_v) \ +consteval auto OP_SYMBOL(const verified_type_basis, \ + const verified_type_basis) \ +{ \ + static_assert(dependent_false, \ + "Can not perform " OP_NAME " between verified types with different basis types"); \ + return verified_type_basis(LHSBasis{}); \ +} + +BOOST_SAFE_NUMBERS_DEFINE_MIXED_VERIFIED_OP("addition", operator+) +BOOST_SAFE_NUMBERS_DEFINE_MIXED_VERIFIED_OP("subtraction", operator-) +BOOST_SAFE_NUMBERS_DEFINE_MIXED_VERIFIED_OP("multiplication", operator*) +BOOST_SAFE_NUMBERS_DEFINE_MIXED_VERIFIED_OP("division", operator/) +BOOST_SAFE_NUMBERS_DEFINE_MIXED_VERIFIED_OP("modulo", operator%) + +#undef BOOST_SAFE_NUMBERS_DEFINE_MIXED_VERIFIED_OP + +// Mixed-type comparison guard +template + requires (!std::is_same_v) +constexpr auto operator<=>(const verified_type_basis, + const verified_type_basis) + -> std::strong_ordering +{ + static_assert(dependent_false, + "Can not compare verified types with different basis types"); + return std::strong_ordering::equal; +} + +template + requires (!std::is_same_v) +constexpr auto operator==(const verified_type_basis, + const verified_type_basis) + -> bool +{ + static_assert(dependent_false, + "Can not compare verified types with different basis types"); + return false; +} + +} // namespace boost::safe_numbers::detail + +#endif // BOOST_SAFE_NUMBERS_VERIFIED_INTEGER_BASIS_HPP diff --git a/include/boost/safe_numbers/iostream.hpp b/include/boost/safe_numbers/iostream.hpp index dd7f8e3..330c846 100644 --- a/include/boost/safe_numbers/iostream.hpp +++ b/include/boost/safe_numbers/iostream.hpp @@ -25,6 +25,7 @@ namespace boost::safe_numbers::detail { #endif BOOST_SAFE_NUMBERS_EXPORT template + requires (!is_verified_type_v) auto operator>>(std::basic_istream& is, LibType& v) -> std::basic_istream& { using underlying_type = underlying_type_t; diff --git a/include/boost/safe_numbers/limits.hpp b/include/boost/safe_numbers/limits.hpp index 5711fc3..db7c085 100644 --- a/include/boost/safe_numbers/limits.hpp +++ b/include/boost/safe_numbers/limits.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #ifndef BOOST_SAFE_NUMBERS_BUILD_MODULE @@ -136,6 +137,52 @@ class numeric_limits> static constexpr type denorm_min() { return min(); } }; +template +class numeric_limits> +{ + using type = boost::safe_numbers::detail::verified_type_basis; + using basis_limits = std::numeric_limits; + +public: + static constexpr bool is_specialized = basis_limits::is_specialized; + static constexpr bool is_signed = basis_limits::is_signed; + static constexpr bool is_integer = basis_limits::is_integer; + static constexpr bool is_exact = basis_limits::is_exact; + static constexpr bool has_infinity = basis_limits::has_infinity; + static constexpr bool has_quiet_NaN = basis_limits::has_quiet_NaN; + static constexpr bool has_signaling_NaN = basis_limits::has_signaling_NaN; + + #if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) + static constexpr std::float_denorm_style has_denorm = basis_limits::has_denorm; + static constexpr bool has_denorm_loss = basis_limits::has_denorm_loss; + #endif + + static constexpr std::float_round_style round_style = basis_limits::round_style; + static constexpr bool is_iec559 = basis_limits::is_iec559; + static constexpr bool is_bounded = basis_limits::is_bounded; + static constexpr bool is_modulo = basis_limits::is_modulo; + static constexpr int digits = basis_limits::digits; + static constexpr int digits10 = basis_limits::digits10; + static constexpr int max_digits10 = basis_limits::max_digits10; + static constexpr int radix = basis_limits::radix; + static constexpr int min_exponent = basis_limits::min_exponent; + static constexpr int min_exponent10 = basis_limits::min_exponent10; + static constexpr int max_exponent = basis_limits::max_exponent; + static constexpr int max_exponent10 = basis_limits::max_exponent10; + static constexpr bool traps = basis_limits::traps; + static constexpr bool tinyness_before = basis_limits::tinyness_before; + + static constexpr type min() { return type{basis_limits::min()}; } + static constexpr type max() { return type{basis_limits::max()}; } + static constexpr type lowest() { return type{basis_limits::lowest()}; } + static constexpr type epsilon() { return type{basis_limits::epsilon()}; } + static constexpr type round_error() { return type{basis_limits::round_error()}; } + static constexpr type infinity() { return type{basis_limits::infinity()}; } + static constexpr type quiet_NaN() { return type{basis_limits::quiet_NaN()}; } + static constexpr type signaling_NaN() { return type{basis_limits::signaling_NaN()}; } + static constexpr type denorm_min() { return type{basis_limits::denorm_min()}; } +}; + #ifdef __clang__ # pragma clang diagnostic pop #endif diff --git a/include/boost/safe_numbers/verified_integers.hpp b/include/boost/safe_numbers/verified_integers.hpp new file mode 100644 index 0000000..169db9b --- /dev/null +++ b/include/boost/safe_numbers/verified_integers.hpp @@ -0,0 +1,29 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_SAFE_NUMBERS_VERIFIED_INTEGERS_HPP +#define BOOST_SAFE_NUMBERS_VERIFIED_INTEGERS_HPP + +#include +#include +#include + +namespace boost::safe_numbers { + +BOOST_SAFE_NUMBERS_EXPORT using verified_u8 = detail::verified_type_basis; + +BOOST_SAFE_NUMBERS_EXPORT using verified_u16 = detail::verified_type_basis; + +BOOST_SAFE_NUMBERS_EXPORT using verified_u32 = detail::verified_type_basis; + +BOOST_SAFE_NUMBERS_EXPORT using verified_u64 = detail::verified_type_basis; + +BOOST_SAFE_NUMBERS_EXPORT using verified_u128 = detail::verified_type_basis; + +BOOST_SAFE_NUMBERS_EXPORT template +using verified_bounded_integer = detail::verified_type_basis>; + +} // boost::safe_numbers + +#endif // BOOST_SAFE_NUMBERS_VERIFIED_INTEGERS_HPP diff --git a/test/Jamfile b/test/Jamfile index b0e7ae4..1a46004 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -117,6 +117,14 @@ run test_unsigned_bounded_std_format.cpp ; compile-fail compile_fail_bounded_mixed_ops.cpp ; compile-fail compile_fail_bounded_bool_bounds.cpp ; +run test_verified_integers.cpp ; +run test_verified_streaming.cpp ; +run test_verified_bit.cpp ; +run test_verified_charconv.cpp ; +run test_verified_fmt_format.cpp ; +run test_verified_std_format.cpp ; +run test_verified_limits.cpp ; + # Compile Tests compile compile_tests/compile_test_unsigned_integers.cpp ; compile compile_tests/compile_test_iostream.cpp ; @@ -144,3 +152,7 @@ run ../examples/charconv.cpp ; run ../examples/bit.cpp ; run ../examples/bounded_limits.cpp ; run ../examples/bitwise_ops.cpp ; +run ../examples/verified_construction.cpp ; +run ../examples/verified_arithmetic.cpp ; +run ../examples/verified_runtime_usage.cpp ; +compile-fail ../examples/compile_fail_verified_overflow.cpp ; diff --git a/test/test_verified_bit.cpp b/test/test_verified_bit.cpp new file mode 100644 index 0000000..e4a7309 --- /dev/null +++ b/test/test_verified_bit.cpp @@ -0,0 +1,286 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifdef BOOST_SAFE_NUMBERS_BUILD_MODULE + +import boost.safe_numbers; + +#else + +#include +#include +#include + +#endif + +#include + +using namespace boost::safe_numbers; + +// ============================================================================= +// has_single_bit (returns bool - runtime testable) +// ============================================================================= + +template +void test_has_single_bit() +{ + BOOST_TEST_EQ(boost::safe_numbers::has_single_bit(VerifiedT{BasisT{0}}), false); + BOOST_TEST_EQ(boost::safe_numbers::has_single_bit(VerifiedT{BasisT{1}}), true); + BOOST_TEST_EQ(boost::safe_numbers::has_single_bit(VerifiedT{BasisT{2}}), true); + BOOST_TEST_EQ(boost::safe_numbers::has_single_bit(VerifiedT{BasisT{3}}), false); + BOOST_TEST_EQ(boost::safe_numbers::has_single_bit(VerifiedT{BasisT{4}}), true); + BOOST_TEST_EQ(boost::safe_numbers::has_single_bit(VerifiedT{BasisT{42}}), false); + BOOST_TEST_EQ(boost::safe_numbers::has_single_bit(VerifiedT{BasisT{64}}), true); + BOOST_TEST_EQ(boost::safe_numbers::has_single_bit(VerifiedT{BasisT{128}}), true); +} + +// ============================================================================= +// bit_width (returns int - runtime testable) +// ============================================================================= + +template +void test_bit_width() +{ + // Compare verified result against basis type result + BOOST_TEST_EQ(boost::safe_numbers::bit_width(VerifiedT{BasisT{0}}), + boost::safe_numbers::bit_width(BasisT{0})); + BOOST_TEST_EQ(boost::safe_numbers::bit_width(VerifiedT{BasisT{1}}), + boost::safe_numbers::bit_width(BasisT{1})); + BOOST_TEST_EQ(boost::safe_numbers::bit_width(VerifiedT{BasisT{42}}), + boost::safe_numbers::bit_width(BasisT{42})); + BOOST_TEST_EQ(boost::safe_numbers::bit_width(VerifiedT{BasisT{255}}), + boost::safe_numbers::bit_width(BasisT{255})); +} + +// ============================================================================= +// countl_zero (returns int - runtime testable) +// ============================================================================= + +template +void test_countl_zero() +{ + BOOST_TEST_EQ(boost::safe_numbers::countl_zero(VerifiedT{BasisT{0}}), + boost::safe_numbers::countl_zero(BasisT{0})); + BOOST_TEST_EQ(boost::safe_numbers::countl_zero(VerifiedT{BasisT{1}}), + boost::safe_numbers::countl_zero(BasisT{1})); + BOOST_TEST_EQ(boost::safe_numbers::countl_zero(VerifiedT{BasisT{42}}), + boost::safe_numbers::countl_zero(BasisT{42})); + BOOST_TEST_EQ(boost::safe_numbers::countl_zero(VerifiedT{BasisT{128}}), + boost::safe_numbers::countl_zero(BasisT{128})); +} + +// ============================================================================= +// countl_one (returns int - runtime testable) +// ============================================================================= + +template +void test_countl_one() +{ + BOOST_TEST_EQ(boost::safe_numbers::countl_one(VerifiedT{BasisT{0}}), + boost::safe_numbers::countl_one(BasisT{0})); + BOOST_TEST_EQ(boost::safe_numbers::countl_one(VerifiedT{BasisT{1}}), + boost::safe_numbers::countl_one(BasisT{1})); + BOOST_TEST_EQ(boost::safe_numbers::countl_one(VerifiedT{BasisT{42}}), + boost::safe_numbers::countl_one(BasisT{42})); + BOOST_TEST_EQ(boost::safe_numbers::countl_one(VerifiedT{BasisT{255}}), + boost::safe_numbers::countl_one(BasisT{255})); +} + +// ============================================================================= +// countr_zero (returns int - runtime testable) +// ============================================================================= + +template +void test_countr_zero() +{ + BOOST_TEST_EQ(boost::safe_numbers::countr_zero(VerifiedT{BasisT{0}}), + boost::safe_numbers::countr_zero(BasisT{0})); + BOOST_TEST_EQ(boost::safe_numbers::countr_zero(VerifiedT{BasisT{1}}), + boost::safe_numbers::countr_zero(BasisT{1})); + BOOST_TEST_EQ(boost::safe_numbers::countr_zero(VerifiedT{BasisT{42}}), + boost::safe_numbers::countr_zero(BasisT{42})); + BOOST_TEST_EQ(boost::safe_numbers::countr_zero(VerifiedT{BasisT{128}}), + boost::safe_numbers::countr_zero(BasisT{128})); +} + +// ============================================================================= +// countr_one (returns int - runtime testable) +// ============================================================================= + +template +void test_countr_one() +{ + BOOST_TEST_EQ(boost::safe_numbers::countr_one(VerifiedT{BasisT{0}}), + boost::safe_numbers::countr_one(BasisT{0})); + BOOST_TEST_EQ(boost::safe_numbers::countr_one(VerifiedT{BasisT{1}}), + boost::safe_numbers::countr_one(BasisT{1})); + BOOST_TEST_EQ(boost::safe_numbers::countr_one(VerifiedT{BasisT{42}}), + boost::safe_numbers::countr_one(BasisT{42})); + BOOST_TEST_EQ(boost::safe_numbers::countr_one(VerifiedT{BasisT{255}}), + boost::safe_numbers::countr_one(BasisT{255})); +} + +// ============================================================================= +// popcount (returns int - runtime testable) +// ============================================================================= + +template +void test_popcount() +{ + BOOST_TEST_EQ(boost::safe_numbers::popcount(VerifiedT{BasisT{0}}), + boost::safe_numbers::popcount(BasisT{0})); + BOOST_TEST_EQ(boost::safe_numbers::popcount(VerifiedT{BasisT{1}}), + boost::safe_numbers::popcount(BasisT{1})); + BOOST_TEST_EQ(boost::safe_numbers::popcount(VerifiedT{BasisT{42}}), + boost::safe_numbers::popcount(BasisT{42})); + BOOST_TEST_EQ(boost::safe_numbers::popcount(VerifiedT{BasisT{255}}), + boost::safe_numbers::popcount(BasisT{255})); +} + +// ============================================================================= +// bit_ceil (returns UnsignedInt - compile-time only for verified types) +// ============================================================================= + +template +consteval auto test_bit_ceil_impl() -> bool +{ + return boost::safe_numbers::bit_ceil(VerifiedT{BasisT{1}}) == VerifiedT{BasisT{1}} + && boost::safe_numbers::bit_ceil(VerifiedT{BasisT{2}}) == VerifiedT{BasisT{2}} + && boost::safe_numbers::bit_ceil(VerifiedT{BasisT{3}}) == VerifiedT{BasisT{4}} + && boost::safe_numbers::bit_ceil(VerifiedT{BasisT{5}}) == VerifiedT{BasisT{8}} + && boost::safe_numbers::bit_ceil(VerifiedT{BasisT{42}}) == VerifiedT{BasisT{64}} + && boost::safe_numbers::bit_ceil(VerifiedT{BasisT{128}}) == VerifiedT{BasisT{128}}; +} + +static_assert(test_bit_ceil_impl()); +static_assert(test_bit_ceil_impl()); +static_assert(test_bit_ceil_impl()); +static_assert(test_bit_ceil_impl()); +static_assert(test_bit_ceil_impl()); + +// ============================================================================= +// bit_floor (returns UnsignedInt - compile-time only for verified types) +// ============================================================================= + +template +consteval auto test_bit_floor_impl() -> bool +{ + return boost::safe_numbers::bit_floor(VerifiedT{BasisT{0}}) == VerifiedT{BasisT{0}} + && boost::safe_numbers::bit_floor(VerifiedT{BasisT{1}}) == VerifiedT{BasisT{1}} + && boost::safe_numbers::bit_floor(VerifiedT{BasisT{3}}) == VerifiedT{BasisT{2}} + && boost::safe_numbers::bit_floor(VerifiedT{BasisT{7}}) == VerifiedT{BasisT{4}} + && boost::safe_numbers::bit_floor(VerifiedT{BasisT{42}}) == VerifiedT{BasisT{32}} + && boost::safe_numbers::bit_floor(VerifiedT{BasisT{128}}) == VerifiedT{BasisT{128}}; +} + +static_assert(test_bit_floor_impl()); +static_assert(test_bit_floor_impl()); +static_assert(test_bit_floor_impl()); +static_assert(test_bit_floor_impl()); +static_assert(test_bit_floor_impl()); + +// ============================================================================= +// rotl (returns UnsignedInt - compile-time only, non-bounded only) +// ============================================================================= + +template +consteval auto test_rotl_impl() -> bool +{ + // rotl(1, 0) == 1 + // rotl(1, 1) == 2 + return boost::safe_numbers::rotl(VerifiedT{BasisT{1}}, 0) == VerifiedT{BasisT{1}} + && boost::safe_numbers::rotl(VerifiedT{BasisT{1}}, 1) == VerifiedT{BasisT{2}}; +} + +static_assert(test_rotl_impl()); +static_assert(test_rotl_impl()); +static_assert(test_rotl_impl()); +static_assert(test_rotl_impl()); +static_assert(test_rotl_impl()); + +// ============================================================================= +// rotr (returns UnsignedInt - compile-time only, non-bounded only) +// ============================================================================= + +template +consteval auto test_rotr_impl() -> bool +{ + // rotr(2, 1) == 1 + return boost::safe_numbers::rotr(VerifiedT{BasisT{2}}, 0) == VerifiedT{BasisT{2}} + && boost::safe_numbers::rotr(VerifiedT{BasisT{2}}, 1) == VerifiedT{BasisT{1}}; +} + +static_assert(test_rotr_impl()); +static_assert(test_rotr_impl()); +static_assert(test_rotr_impl()); +static_assert(test_rotr_impl()); +static_assert(test_rotr_impl()); + +// ============================================================================= +// byteswap (returns UnsignedInt - compile-time only, non-bounded only) +// ============================================================================= + +template +consteval auto test_byteswap_impl() -> bool +{ + // byteswap(0) == 0 for any width + return boost::safe_numbers::byteswap(VerifiedT{BasisT{0}}) == VerifiedT{BasisT{0}}; +} + +static_assert(test_byteswap_impl()); +static_assert(test_byteswap_impl()); +static_assert(test_byteswap_impl()); +static_assert(test_byteswap_impl()); +static_assert(test_byteswap_impl()); + +// ============================================================================= +// Main - runtime tests for functions returning int/bool +// ============================================================================= + +int main() +{ + test_has_single_bit(); + test_has_single_bit(); + test_has_single_bit(); + test_has_single_bit(); + test_has_single_bit(); + + test_bit_width(); + test_bit_width(); + test_bit_width(); + test_bit_width(); + test_bit_width(); + + test_countl_zero(); + test_countl_zero(); + test_countl_zero(); + test_countl_zero(); + test_countl_zero(); + + test_countl_one(); + test_countl_one(); + test_countl_one(); + test_countl_one(); + test_countl_one(); + + test_countr_zero(); + test_countr_zero(); + test_countr_zero(); + test_countr_zero(); + test_countr_zero(); + + test_countr_one(); + test_countr_one(); + test_countr_one(); + test_countr_one(); + test_countr_one(); + + test_popcount(); + test_popcount(); + test_popcount(); + test_popcount(); + test_popcount(); + + return boost::report_errors(); +} diff --git a/test/test_verified_charconv.cpp b/test/test_verified_charconv.cpp new file mode 100644 index 0000000..21e8efa --- /dev/null +++ b/test/test_verified_charconv.cpp @@ -0,0 +1,199 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifdef BOOST_SAFE_NUMBERS_BUILD_MODULE + +import boost.safe_numbers; + +#else + +#include +#include +#include + +#endif + +#include + +using namespace boost::safe_numbers; + +// ============================================================================= +// to_chars tests (constexpr - runtime testable with verified types) +// ============================================================================= + +template +void test_to_chars() +{ + constexpr auto val = VerifiedT{BasisT{42}}; + + char buffer[256]; + const auto r = boost::safe_numbers::to_chars(buffer, buffer + sizeof(buffer), val); + BOOST_TEST(r.ec == std::errc{}); + + const auto len = static_cast(r.ptr - buffer); + BOOST_TEST_EQ(len, std::size_t{2}); + BOOST_TEST_EQ(buffer[0], '4'); + BOOST_TEST_EQ(buffer[1], '2'); +} + +template +void test_to_chars_zero() +{ + constexpr auto val = VerifiedT{BasisT{0}}; + + char buffer[256]; + const auto r = boost::safe_numbers::to_chars(buffer, buffer + sizeof(buffer), val); + BOOST_TEST(r.ec == std::errc{}); + + const auto len = static_cast(r.ptr - buffer); + BOOST_TEST_EQ(len, std::size_t{1}); + BOOST_TEST_EQ(buffer[0], '0'); +} + +template +void test_to_chars_255() +{ + constexpr auto val = VerifiedT{BasisT{255}}; + + char buffer[256]; + const auto r = boost::safe_numbers::to_chars(buffer, buffer + sizeof(buffer), val); + BOOST_TEST(r.ec == std::errc{}); + + const auto len = static_cast(r.ptr - buffer); + BOOST_TEST_EQ(len, std::size_t{3}); + BOOST_TEST_EQ(buffer[0], '2'); + BOOST_TEST_EQ(buffer[1], '5'); + BOOST_TEST_EQ(buffer[2], '5'); +} + +template +void test_to_chars_hex() +{ + constexpr auto val = VerifiedT{BasisT{255}}; + + char buffer[256]; + const auto r = boost::safe_numbers::to_chars(buffer, buffer + sizeof(buffer), val, 16); + BOOST_TEST(r.ec == std::errc{}); + + const auto len = static_cast(r.ptr - buffer); + BOOST_TEST_EQ(len, std::size_t{2}); + BOOST_TEST_EQ(buffer[0], 'f'); + BOOST_TEST_EQ(buffer[1], 'f'); +} + +// ============================================================================= +// from_chars tests (consteval - compile-time only for verified types) +// ============================================================================= + +template +consteval auto test_from_chars_impl() -> bool +{ + const char str[] = "42"; + auto val = VerifiedT{BasisT{0}}; + const auto r = boost::safe_numbers::from_chars(str, str + 2, val); + return r.ec == std::errc{} && val == VerifiedT{BasisT{42}}; +} + +template +consteval auto test_from_chars_zero_impl() -> bool +{ + const char str[] = "0"; + auto val = VerifiedT{BasisT{1}}; + const auto r = boost::safe_numbers::from_chars(str, str + 1, val); + return r.ec == std::errc{} && val == VerifiedT{BasisT{0}}; +} + +template +consteval auto test_from_chars_255_impl() -> bool +{ + const char str[] = "255"; + auto val = VerifiedT{BasisT{0}}; + const auto r = boost::safe_numbers::from_chars(str, str + 3, val); + return r.ec == std::errc{} && val == VerifiedT{BasisT{255}}; +} + +template +consteval auto test_from_chars_hex_impl() -> bool +{ + const char str[] = "ff"; + auto val = VerifiedT{BasisT{0}}; + const auto r = boost::safe_numbers::from_chars(str, str + 2, val, 16); + return r.ec == std::errc{} && val == VerifiedT{BasisT{255}}; +} + +// --- from_chars base 10 --- +static_assert(test_from_chars_impl()); +static_assert(test_from_chars_impl()); +static_assert(test_from_chars_impl()); +static_assert(test_from_chars_impl()); +static_assert(test_from_chars_impl()); + +// --- from_chars zero --- +static_assert(test_from_chars_zero_impl()); +static_assert(test_from_chars_zero_impl()); +static_assert(test_from_chars_zero_impl()); +static_assert(test_from_chars_zero_impl()); +static_assert(test_from_chars_zero_impl()); + +// --- from_chars 255 --- +static_assert(test_from_chars_255_impl()); +static_assert(test_from_chars_255_impl()); +static_assert(test_from_chars_255_impl()); +static_assert(test_from_chars_255_impl()); +static_assert(test_from_chars_255_impl()); + +// --- from_chars hex --- +static_assert(test_from_chars_hex_impl()); +static_assert(test_from_chars_hex_impl()); +static_assert(test_from_chars_hex_impl()); +static_assert(test_from_chars_hex_impl()); +static_assert(test_from_chars_hex_impl()); + +// --- from_chars with bounded type --- +using test_bounded = bounded_uint<0u, 255u>; +using test_verified_bounded = verified_bounded_integer<0u, 255u>; + +static_assert(test_from_chars_impl()); +static_assert(test_from_chars_zero_impl()); +static_assert(test_from_chars_255_impl()); +static_assert(test_from_chars_hex_impl()); + +// ============================================================================= +// Main - runtime tests for to_chars +// ============================================================================= + +int main() +{ + test_to_chars(); + test_to_chars(); + test_to_chars(); + test_to_chars(); + test_to_chars(); + + test_to_chars_zero(); + test_to_chars_zero(); + test_to_chars_zero(); + test_to_chars_zero(); + test_to_chars_zero(); + + test_to_chars_255(); + test_to_chars_255(); + test_to_chars_255(); + test_to_chars_255(); + test_to_chars_255(); + + test_to_chars_hex(); + test_to_chars_hex(); + test_to_chars_hex(); + test_to_chars_hex(); + test_to_chars_hex(); + + // Bounded verified types + test_to_chars(); + test_to_chars_zero(); + test_to_chars_255(); + test_to_chars_hex(); + + return boost::report_errors(); +} diff --git a/test/test_verified_fmt_format.cpp b/test/test_verified_fmt_format.cpp new file mode 100644 index 0000000..29be232 --- /dev/null +++ b/test/test_verified_fmt_format.cpp @@ -0,0 +1,91 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +# pragma GCC diagnostic ignored "-Wsign-conversion" +# pragma GCC diagnostic ignored "-Wconversion" +#endif + +#define FMT_HEADER_ONLY + +#if __has_include() + +#include +#include +#include +#include + +using namespace boost::safe_numbers; + +template +void test() +{ + constexpr auto x = VerifiedT{BasisT{42}}; + + BOOST_TEST_CSTR_EQ(fmt::format("{}", x).c_str(), "42"); + BOOST_TEST_CSTR_EQ(fmt::format("{:08x}", x).c_str(), "0000002a"); + BOOST_TEST_CSTR_EQ(fmt::format("{:#010b}", x).c_str(), "0b00101010"); +} + +template +void test_zero() +{ + constexpr auto x = VerifiedT{BasisT{0}}; + + BOOST_TEST_CSTR_EQ(fmt::format("{}", x).c_str(), "0"); +} + +template +void test_255() +{ + constexpr auto x = VerifiedT{BasisT{255}}; + + BOOST_TEST_CSTR_EQ(fmt::format("{}", x).c_str(), "255"); + BOOST_TEST_CSTR_EQ(fmt::format("{:x}", x).c_str(), "ff"); +} + +int main() +{ + test(); + test(); + test(); + test(); + test(); + + test_zero(); + test_zero(); + test_zero(); + test_zero(); + test_zero(); + + test_255(); + test_255(); + test_255(); + test_255(); + test_255(); + + // Bounded verified type + using test_bounded = bounded_uint<0u, 255u>; + using test_verified_bounded = verified_bounded_integer<0u, 255u>; + + test(); + test_zero(); + test_255(); + + return boost::report_errors(); +} + +#else + +int main() +{ + return 0; +} + +#endif diff --git a/test/test_verified_integers.cpp b/test/test_verified_integers.cpp new file mode 100644 index 0000000..2f52652 --- /dev/null +++ b/test/test_verified_integers.cpp @@ -0,0 +1,365 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifdef BOOST_SAFE_NUMBERS_BUILD_MODULE + +import boost.safe_numbers; + +#else + +#include +#include +#include +#include + +#endif + +#include + +using namespace boost::safe_numbers; + +// ============================================================================= +// Construction tests (consteval - verified via constexpr variables) +// ============================================================================= + +template +void test_construction() +{ + constexpr auto val1 = VerifiedT{BasisT{0}}; + constexpr auto val2 = VerifiedT{BasisT{42}}; + constexpr auto val3 = VerifiedT{BasisT{100}}; + + BOOST_TEST(val1 == VerifiedT{BasisT{0}}); + BOOST_TEST(val2 == VerifiedT{BasisT{42}}); + BOOST_TEST(val3 == VerifiedT{BasisT{100}}); + + // From underlying type + using underlying = detail::underlying_type_t; + constexpr auto val4 = VerifiedT{underlying{42}}; + BOOST_TEST(val4 == val2); +} + +// ============================================================================= +// Conversion tests (constexpr - runtime testable) +// ============================================================================= + +template +void test_conversions() +{ + using underlying = detail::underlying_type_t; + + constexpr auto val = VerifiedT{BasisT{42}}; + + // Convert to BasisType + constexpr auto basis_val = static_cast(val); + BOOST_TEST(basis_val == BasisT{42}); + + // Convert to underlying type + constexpr auto raw_val = static_cast(val); + BOOST_TEST(raw_val == underlying{42}); +} + +// ============================================================================= +// Comparison tests (constexpr - runtime testable) +// ============================================================================= + +template +void test_comparisons() +{ + constexpr auto a = VerifiedT{BasisT{10}}; + constexpr auto b = VerifiedT{BasisT{20}}; + constexpr auto c = VerifiedT{BasisT{10}}; + + BOOST_TEST(a == c); + BOOST_TEST(a != b); + BOOST_TEST(a < b); + BOOST_TEST(a <= b); + BOOST_TEST(a <= c); + BOOST_TEST(b > a); + BOOST_TEST(b >= a); + BOOST_TEST(a >= c); + BOOST_TEST((a <=> c) == std::strong_ordering::equal); + BOOST_TEST((a <=> b) == std::strong_ordering::less); + BOOST_TEST((b <=> a) == std::strong_ordering::greater); +} + +// ============================================================================= +// Arithmetic tests (consteval - verified via static_assert) +// ============================================================================= + +template +consteval auto test_addition_impl() -> bool +{ + const auto a = VerifiedT{BasisT{10}}; + const auto b = VerifiedT{BasisT{20}}; + const auto c = a + b; + return c == VerifiedT{BasisT{30}}; +} + +template +consteval auto test_subtraction_impl() -> bool +{ + const auto a = VerifiedT{BasisT{30}}; + const auto b = VerifiedT{BasisT{10}}; + const auto c = a - b; + return c == VerifiedT{BasisT{20}}; +} + +template +consteval auto test_multiplication_impl() -> bool +{ + const auto a = VerifiedT{BasisT{5}}; + const auto b = VerifiedT{BasisT{6}}; + const auto c = a * b; + return c == VerifiedT{BasisT{30}}; +} + +template +consteval auto test_division_impl() -> bool +{ + const auto a = VerifiedT{BasisT{30}}; + const auto b = VerifiedT{BasisT{5}}; + const auto c = a / b; + return c == VerifiedT{BasisT{6}}; +} + +template +consteval auto test_modulo_impl() -> bool +{ + const auto a = VerifiedT{BasisT{17}}; + const auto b = VerifiedT{BasisT{5}}; + const auto c = a % b; + return c == VerifiedT{BasisT{2}}; +} + +// --- Addition --- +static_assert(test_addition_impl()); +static_assert(test_addition_impl()); +static_assert(test_addition_impl()); +static_assert(test_addition_impl()); +static_assert(test_addition_impl()); + +// --- Subtraction --- +static_assert(test_subtraction_impl()); +static_assert(test_subtraction_impl()); +static_assert(test_subtraction_impl()); +static_assert(test_subtraction_impl()); +static_assert(test_subtraction_impl()); + +// --- Multiplication --- +static_assert(test_multiplication_impl()); +static_assert(test_multiplication_impl()); +static_assert(test_multiplication_impl()); +static_assert(test_multiplication_impl()); +static_assert(test_multiplication_impl()); + +// --- Division --- +static_assert(test_division_impl()); +static_assert(test_division_impl()); +static_assert(test_division_impl()); +static_assert(test_division_impl()); +static_assert(test_division_impl()); + +// --- Modulo --- +static_assert(test_modulo_impl()); +static_assert(test_modulo_impl()); +static_assert(test_modulo_impl()); +static_assert(test_modulo_impl()); +static_assert(test_modulo_impl()); + +// ============================================================================= +// Compound assignment tests (consteval - verified via static_assert) +// ============================================================================= + +template +consteval auto test_compound_addition_impl() -> bool +{ + auto a = VerifiedT{BasisT{10}}; + a += VerifiedT{BasisT{20}}; + return a == VerifiedT{BasisT{30}}; +} + +template +consteval auto test_compound_subtraction_impl() -> bool +{ + auto a = VerifiedT{BasisT{30}}; + a -= VerifiedT{BasisT{10}}; + return a == VerifiedT{BasisT{20}}; +} + +template +consteval auto test_compound_multiplication_impl() -> bool +{ + auto a = VerifiedT{BasisT{5}}; + a *= VerifiedT{BasisT{6}}; + return a == VerifiedT{BasisT{30}}; +} + +template +consteval auto test_compound_division_impl() -> bool +{ + auto a = VerifiedT{BasisT{30}}; + a /= VerifiedT{BasisT{5}}; + return a == VerifiedT{BasisT{6}}; +} + +template +consteval auto test_compound_modulo_impl() -> bool +{ + auto a = VerifiedT{BasisT{17}}; + a %= VerifiedT{BasisT{5}}; + return a == VerifiedT{BasisT{2}}; +} + +// --- Compound addition --- +static_assert(test_compound_addition_impl()); +static_assert(test_compound_addition_impl()); +static_assert(test_compound_addition_impl()); +static_assert(test_compound_addition_impl()); +static_assert(test_compound_addition_impl()); + +// --- Compound subtraction --- +static_assert(test_compound_subtraction_impl()); +static_assert(test_compound_subtraction_impl()); +static_assert(test_compound_subtraction_impl()); +static_assert(test_compound_subtraction_impl()); +static_assert(test_compound_subtraction_impl()); + +// --- Compound multiplication --- +static_assert(test_compound_multiplication_impl()); +static_assert(test_compound_multiplication_impl()); +static_assert(test_compound_multiplication_impl()); +static_assert(test_compound_multiplication_impl()); +static_assert(test_compound_multiplication_impl()); + +// --- Compound division --- +static_assert(test_compound_division_impl()); +static_assert(test_compound_division_impl()); +static_assert(test_compound_division_impl()); +static_assert(test_compound_division_impl()); +static_assert(test_compound_division_impl()); + +// --- Compound modulo --- +static_assert(test_compound_modulo_impl()); +static_assert(test_compound_modulo_impl()); +static_assert(test_compound_modulo_impl()); +static_assert(test_compound_modulo_impl()); +static_assert(test_compound_modulo_impl()); + +// ============================================================================= +// Increment/decrement tests (consteval - verified via static_assert) +// ============================================================================= + +template +consteval auto test_prefix_increment_impl() -> bool +{ + auto a = VerifiedT{BasisT{5}}; + ++a; + return a == VerifiedT{BasisT{6}}; +} + +template +consteval auto test_postfix_increment_impl() -> bool +{ + auto a = VerifiedT{BasisT{5}}; + const auto old = a++; + return a == VerifiedT{BasisT{6}} && old == VerifiedT{BasisT{5}}; +} + +template +consteval auto test_prefix_decrement_impl() -> bool +{ + auto a = VerifiedT{BasisT{5}}; + --a; + return a == VerifiedT{BasisT{4}}; +} + +template +consteval auto test_postfix_decrement_impl() -> bool +{ + auto a = VerifiedT{BasisT{5}}; + const auto old = a--; + return a == VerifiedT{BasisT{4}} && old == VerifiedT{BasisT{5}}; +} + +// --- Prefix increment --- +static_assert(test_prefix_increment_impl()); +static_assert(test_prefix_increment_impl()); +static_assert(test_prefix_increment_impl()); +static_assert(test_prefix_increment_impl()); +static_assert(test_prefix_increment_impl()); + +// --- Postfix increment --- +static_assert(test_postfix_increment_impl()); +static_assert(test_postfix_increment_impl()); +static_assert(test_postfix_increment_impl()); +static_assert(test_postfix_increment_impl()); +static_assert(test_postfix_increment_impl()); + +// --- Prefix decrement --- +static_assert(test_prefix_decrement_impl()); +static_assert(test_prefix_decrement_impl()); +static_assert(test_prefix_decrement_impl()); +static_assert(test_prefix_decrement_impl()); +static_assert(test_prefix_decrement_impl()); + +// --- Postfix decrement --- +static_assert(test_postfix_decrement_impl()); +static_assert(test_postfix_decrement_impl()); +static_assert(test_postfix_decrement_impl()); +static_assert(test_postfix_decrement_impl()); +static_assert(test_postfix_decrement_impl()); + +// ============================================================================= +// Bounded integer tests (consteval - verified via static_assert) +// ============================================================================= + +using test_bounded = bounded_uint<0u, 100u>; +using test_verified_bounded = verified_bounded_integer<0u, 100u>; + +static_assert(test_addition_impl()); +static_assert(test_subtraction_impl()); +static_assert(test_multiplication_impl()); +static_assert(test_division_impl()); +static_assert(test_modulo_impl()); + +static_assert(test_compound_addition_impl()); +static_assert(test_compound_subtraction_impl()); +static_assert(test_compound_multiplication_impl()); +static_assert(test_compound_division_impl()); +static_assert(test_compound_modulo_impl()); + +static_assert(test_prefix_increment_impl()); +static_assert(test_postfix_increment_impl()); +static_assert(test_prefix_decrement_impl()); +static_assert(test_postfix_decrement_impl()); + +// ============================================================================= +// Main - runtime tests for constexpr operations (comparisons, conversions) +// ============================================================================= + +int main() +{ + test_construction(); + test_construction(); + test_construction(); + test_construction(); + test_construction(); + + test_conversions(); + test_conversions(); + test_conversions(); + test_conversions(); + test_conversions(); + + test_comparisons(); + test_comparisons(); + test_comparisons(); + test_comparisons(); + test_comparisons(); + + test_comparisons(); + + return boost::report_errors(); +} diff --git a/test/test_verified_limits.cpp b/test/test_verified_limits.cpp new file mode 100644 index 0000000..21ec6e0 --- /dev/null +++ b/test/test_verified_limits.cpp @@ -0,0 +1,175 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifdef BOOST_SAFE_NUMBERS_BUILD_MODULE + +import boost.safe_numbers; + +#else + +#include + +#endif + +#include +#include + +using namespace boost::safe_numbers; + +// ============================================================================= +// Compile-time tests via static_assert +// ============================================================================= + +template +consteval auto test_limits_properties_impl() -> bool +{ + using vlimits = std::numeric_limits; + using blimits = std::numeric_limits; + + return vlimits::is_specialized == blimits::is_specialized && + vlimits::is_signed == blimits::is_signed && + vlimits::is_integer == blimits::is_integer && + vlimits::is_exact == blimits::is_exact && + vlimits::has_infinity == blimits::has_infinity && + vlimits::has_quiet_NaN == blimits::has_quiet_NaN && + vlimits::has_signaling_NaN == blimits::has_signaling_NaN && + vlimits::is_iec559 == blimits::is_iec559 && + vlimits::is_bounded == blimits::is_bounded && + vlimits::is_modulo == blimits::is_modulo && + vlimits::digits == blimits::digits && + vlimits::digits10 == blimits::digits10 && + vlimits::max_digits10 == blimits::max_digits10 && + vlimits::radix == blimits::radix && + vlimits::min_exponent == blimits::min_exponent && + vlimits::min_exponent10 == blimits::min_exponent10 && + vlimits::max_exponent == blimits::max_exponent && + vlimits::max_exponent10 == blimits::max_exponent10 && + vlimits::traps == blimits::traps && + vlimits::tinyness_before == blimits::tinyness_before; +} + +template +consteval auto test_limits_functions_impl() -> bool +{ + using vlimits = std::numeric_limits; + using blimits = std::numeric_limits; + + return vlimits::min() == VerifiedT{blimits::min()} && + vlimits::max() == VerifiedT{blimits::max()} && + vlimits::lowest() == VerifiedT{blimits::lowest()} && + vlimits::epsilon() == VerifiedT{blimits::epsilon()} && + vlimits::round_error() == VerifiedT{blimits::round_error()} && + vlimits::infinity() == VerifiedT{blimits::infinity()} && + vlimits::quiet_NaN() == VerifiedT{blimits::quiet_NaN()} && + vlimits::signaling_NaN() == VerifiedT{blimits::signaling_NaN()} && + vlimits::denorm_min() == VerifiedT{blimits::denorm_min()}; +} + +// --- Properties --- +static_assert(test_limits_properties_impl()); +static_assert(test_limits_properties_impl()); +static_assert(test_limits_properties_impl()); +static_assert(test_limits_properties_impl()); +static_assert(test_limits_properties_impl()); + +// --- Functions --- +static_assert(test_limits_functions_impl()); +static_assert(test_limits_functions_impl()); +static_assert(test_limits_functions_impl()); +static_assert(test_limits_functions_impl()); +static_assert(test_limits_functions_impl()); + +// --- Bounded verified types --- +using test_bounded = bounded_uint<0u, 255u>; +using test_verified_bounded = verified_bounded_integer<0u, 255u>; + +static_assert(test_limits_properties_impl()); +static_assert(test_limits_functions_impl()); + +using test_bounded_narrow = bounded_uint<10u, 200u>; +using test_verified_bounded_narrow = verified_bounded_integer<10u, 200u>; + +static_assert(test_limits_properties_impl()); +static_assert(test_limits_functions_impl()); + +// ============================================================================= +// Runtime tests +// ============================================================================= + +template +void test_properties() +{ + using vlimits = std::numeric_limits; + using blimits = std::numeric_limits; + + BOOST_TEST_EQ(vlimits::is_specialized, blimits::is_specialized); + BOOST_TEST_EQ(vlimits::is_signed, blimits::is_signed); + BOOST_TEST_EQ(vlimits::is_integer, blimits::is_integer); + BOOST_TEST_EQ(vlimits::is_exact, blimits::is_exact); + BOOST_TEST_EQ(vlimits::has_infinity, blimits::has_infinity); + BOOST_TEST_EQ(vlimits::has_quiet_NaN, blimits::has_quiet_NaN); + BOOST_TEST_EQ(vlimits::has_signaling_NaN, blimits::has_signaling_NaN); + + #if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) + BOOST_TEST(vlimits::has_denorm == blimits::has_denorm); + BOOST_TEST(vlimits::has_denorm_loss == blimits::has_denorm_loss); + #endif + + BOOST_TEST(vlimits::round_style == blimits::round_style); + + BOOST_TEST_EQ(vlimits::is_iec559, blimits::is_iec559); + BOOST_TEST_EQ(vlimits::is_bounded, blimits::is_bounded); + BOOST_TEST_EQ(vlimits::is_modulo, blimits::is_modulo); + BOOST_TEST_EQ(vlimits::digits, blimits::digits); + BOOST_TEST_EQ(vlimits::digits10, blimits::digits10); + BOOST_TEST_EQ(vlimits::max_digits10, blimits::max_digits10); + BOOST_TEST_EQ(vlimits::radix, blimits::radix); + BOOST_TEST_EQ(vlimits::min_exponent, blimits::min_exponent); + BOOST_TEST_EQ(vlimits::min_exponent10, blimits::min_exponent10); + BOOST_TEST_EQ(vlimits::max_exponent, blimits::max_exponent); + BOOST_TEST_EQ(vlimits::max_exponent10, blimits::max_exponent10); + BOOST_TEST_EQ(vlimits::traps, blimits::traps); + BOOST_TEST_EQ(vlimits::tinyness_before, blimits::tinyness_before); +} + +template +void test_functions() +{ + using vlimits = std::numeric_limits; + using blimits = std::numeric_limits; + + BOOST_TEST(vlimits::min() == VerifiedT{blimits::min()}); + BOOST_TEST(vlimits::max() == VerifiedT{blimits::max()}); + BOOST_TEST(vlimits::lowest() == VerifiedT{blimits::lowest()}); + BOOST_TEST(vlimits::epsilon() == VerifiedT{blimits::epsilon()}); + BOOST_TEST(vlimits::round_error() == VerifiedT{blimits::round_error()}); + BOOST_TEST(vlimits::infinity() == VerifiedT{blimits::infinity()}); + BOOST_TEST(vlimits::quiet_NaN() == VerifiedT{blimits::quiet_NaN()}); + BOOST_TEST(vlimits::signaling_NaN() == VerifiedT{blimits::signaling_NaN()}); + BOOST_TEST(vlimits::denorm_min() == VerifiedT{blimits::denorm_min()}); +} + +int main() +{ + test_properties(); + test_properties(); + test_properties(); + test_properties(); + test_properties(); + + test_functions(); + test_functions(); + test_functions(); + test_functions(); + test_functions(); + + // Bounded verified types + test_properties(); + test_functions(); + + test_properties(); + test_functions(); + + return boost::report_errors(); +} diff --git a/test/test_verified_std_format.cpp b/test/test_verified_std_format.cpp new file mode 100644 index 0000000..a7683dd --- /dev/null +++ b/test/test_verified_std_format.cpp @@ -0,0 +1,92 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifdef BOOST_SAFE_NUMBERS_BUILD_MODULE + +import boost.safe_numbers; + +#else + +#include + +#ifdef BOOST_SAFE_NUMBERS_DETAIL_INT128_HAS_FORMAT + +#include + +#endif + +#endif + +#ifdef BOOST_SAFE_NUMBERS_DETAIL_INT128_HAS_FORMAT + +#include + +using namespace boost::safe_numbers; + +template +void test() +{ + constexpr auto x = VerifiedT{BasisT{42}}; + + BOOST_TEST_CSTR_EQ(std::format("{}", x).c_str(), "42"); + BOOST_TEST_CSTR_EQ(std::format("{:08x}", x).c_str(), "0000002a"); + BOOST_TEST_CSTR_EQ(std::format("{:#010b}", x).c_str(), "0b00101010"); +} + +template +void test_zero() +{ + constexpr auto x = VerifiedT{BasisT{0}}; + + BOOST_TEST_CSTR_EQ(std::format("{}", x).c_str(), "0"); +} + +template +void test_255() +{ + constexpr auto x = VerifiedT{BasisT{255}}; + + BOOST_TEST_CSTR_EQ(std::format("{}", x).c_str(), "255"); + BOOST_TEST_CSTR_EQ(std::format("{:x}", x).c_str(), "ff"); +} + +int main() +{ + test(); + test(); + test(); + test(); + test(); + + test_zero(); + test_zero(); + test_zero(); + test_zero(); + test_zero(); + + test_255(); + test_255(); + test_255(); + test_255(); + test_255(); + + // Bounded verified type + using test_bounded = bounded_uint<0u, 255u>; + using test_verified_bounded = verified_bounded_integer<0u, 255u>; + + test(); + test_zero(); + test_255(); + + return boost::report_errors(); +} + +#else + +int main() +{ + return 0; +} + +#endif diff --git a/test/test_verified_streaming.cpp b/test/test_verified_streaming.cpp new file mode 100644 index 0000000..a518990 --- /dev/null +++ b/test/test_verified_streaming.cpp @@ -0,0 +1,77 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include + +using namespace boost::safe_numbers; + +// Verified types are consteval for construction, so only output streaming +// (operator<<) is testable. Input streaming (operator>>) would require +// runtime construction which is not possible with consteval constructors. + +template +void test_output() +{ + constexpr auto val = VerifiedT{BasisT{42}}; + + std::stringstream out; + out << val; + + BOOST_TEST_CSTR_EQ(out.str().c_str(), "42"); +} + +template +void test_output_zero() +{ + constexpr auto val = VerifiedT{BasisT{0}}; + + std::stringstream out; + out << val; + + BOOST_TEST_CSTR_EQ(out.str().c_str(), "0"); +} + +template +void test_output_large() +{ + constexpr auto val = VerifiedT{BasisT{255}}; + + std::stringstream out; + out << val; + + BOOST_TEST_CSTR_EQ(out.str().c_str(), "255"); +} + +int main() +{ + test_output(); + test_output(); + test_output(); + test_output(); + test_output(); + + test_output_zero(); + test_output_zero(); + test_output_zero(); + test_output_zero(); + test_output_zero(); + + test_output_large(); + test_output_large(); + test_output_large(); + test_output_large(); + test_output_large(); + + // Bounded verified type + using test_bounded = bounded_uint<0u, 255u>; + using test_verified_bounded = verified_bounded_integer<0u, 255u>; + + test_output(); + test_output_zero(); + test_output_large(); + + return boost::report_errors(); +}