All the following examples can be found in the examples/ folder of the library.
link:example$basic_usage.cpp[role=include]Output:
Error Detected: Overflow detected in unsigned addition
boost::diagnostic_information to extract the throw-site source location captured by BOOST_THROW_EXCEPTION. On x86_64, it additionally uses boost::stacktrace::from_current_exception() to print a full stacktrace.link:example$basic_usage_stacktrace.cpp[role=include]Output on Linux x64:
./boost/safe_numbers/detail/unsigned_integer_basis.hpp(278): Throw in function boost::safe_numbers::detail::add_helper<boost::safe_numbers::overflow_policy::throw_exception, unsigned char>::apply(boost::safe_numbers::detail::unsigned_integer_basis<unsigned char>, boost::safe_numbers::detail::unsigned_integer_basis<unsigned char>)::<lambda()> Dynamic exception type: boost::wrapexcept<std::overflow_error> std::exception::what: Overflow detected in u8 addition Stacktrace: 0# 0x000000000000B19C in bin.v2/libs/safe_numbers/test/basic_usage_stacktrace.test/gcc-11/debug/x86_64/cxxstd-20-iso/threading-multi/visibility-hidden/basic_usage_stacktrace 1# 0x00000000000099D0 in bin.v2/libs/safe_numbers/test/basic_usage_stacktrace.test/gcc-11/debug/x86_64/cxxstd-20-iso/threading-multi/visibility-hidden/basic_usage_stacktrace 2# 0x0000000000009AE0 in bin.v2/libs/safe_numbers/test/basic_usage_stacktrace.test/gcc-11/debug/x86_64/cxxstd-20-iso/threading-multi/visibility-hidden/basic_usage_stacktrace 3# 0x000000000000820A in bin.v2/libs/safe_numbers/test/basic_usage_stacktrace.test/gcc-11/debug/x86_64/cxxstd-20-iso/threading-multi/visibility-hidden/basic_usage_stacktrace 4# 0x0000000000003A1E in bin.v2/libs/safe_numbers/test/basic_usage_stacktrace.test/gcc-11/debug/x86_64/cxxstd-20-iso/threading-multi/visibility-hidden/basic_usage_stacktrace 5# 0x0000000000029D90 in /lib/x86_64-linux-gnu/libc.so.6 6# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6 7# 0x0000000000003925 in bin.v2/libs/safe_numbers/test/basic_usage_stacktrace.test/gcc-11/debug/x86_64/cxxstd-20-iso/threading-multi/visibility-hidden/basic_usage_stacktrace
Output on MSVC x64
D:\a\safe_numbers\boost-root\boost/safe_numbers/detail/unsigned_integer_basis.hpp(278): Throw in function void __cdecl boost::safe_numbers::detail::add_helper<0,unsigned char>::apply::<lambda_1>::operator ()(void) const Dynamic exception type: struct boost::wrapexcept<class std::overflow_error> std::exception::what: Overflow detected in u8 addition Stacktrace: 0# RtlDispatchException in C:\Windows\SYSTEM32\ntdll.dll 1# KiUserExceptionDispatch in C:\Windows\SYSTEM32\ntdll.dll 2# RaiseException in C:\Windows\System32\KERNELBASE.dll 3# _CxxThrowException at D:\a\_work\1\s\src\vctools\crt\vcruntime\src\eh\throw.cpp:79 4# boost::throw_exception<std::overflow_error> at D:\a\safe_numbers\boost-root\boost\throw_exception.hpp:171 5# `boost::safe_numbers::detail::add_helper<0,unsigned char>::apply'::`2'::<lambda_1>::operator() at D:\a\safe_numbers\boost-root\boost\safe_numbers\detail\unsigned_integer_basis.hpp:278 6# boost::safe_numbers::detail::add_helper<0,unsigned char>::apply at D:\a\safe_numbers\boost-root\boost\safe_numbers\detail\unsigned_integer_basis.hpp:318 7# boost::safe_numbers::detail::operator+<unsigned char> at D:\a\safe_numbers\boost-root\boost\safe_numbers\detail\unsigned_integer_basis.hpp:486 8# main at D:\a\safe_numbers\boost-root\libs\safe_numbers\examples\basic_usage_stacktrace.cpp:24 9# invoke_main at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:79 10# __scrt_common_main_seh at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288 11# __scrt_common_main at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:331 12# mainCRTStartup at D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp:17 13# BaseThreadInitThunk in C:\Windows\System32\KERNEL32.DLL 14# RtlUserThreadStart in C:\Windows\SYSTEM32\ntdll.dll
Output on Other Platforms:
./boost/safe_numbers/detail/unsigned_integer_basis.hpp(278): Throw in function ... Dynamic exception type: boost::wrapexcept<std::overflow_error> std::exception::what: Overflow detected in u8 addition
link:example$basic_bounded_usage.cpp[role=include]Output:
Error Detected: bounded_uint<0, 100> addition result out of range
link:example$construction_and_conversion.cpp[role=include]Output:
Safe Value: 42 Builtin Value: 42
Saturating arithmetic clamps results to the type’s minimum or maximum value instead of overflowing or throwing. This is useful when you want bounded behavior without exceptions.
link:example$saturating_arithmetic.cpp[role=include]Output:
saturating_add(max, 100) = 4294967295 saturating_sub(10, 100) = 0 saturating_mul(max, 2) = 4294967295 saturating_add(100, 50) = 150 saturating_sub(100, 50) = 50 saturating_mul(100, 50) = 5000
Overflowing arithmetic returns both the wrapped result and a boolean flag indicating whether overflow occurred. This gives you access to both the C-style wrapped value and overflow detection in a single operation.
link:example$overflowing_arithmetic.cpp[role=include]Output:
overflowing_add(max, 100): result = 99 overflowed = true overflowing_sub(10, 100): result = 4294967206 underflowed = true overflowing_mul(max, 2): result = 4294967294 overflowed = true overflowing_add(100, 50) = 150 (overflow: false) overflowing_sub(100, 50) = 50 (overflow: false) overflowing_mul(100, 50) = 5000 (overflow: false) Safe multiplication: 1000000000 * 5 = 5000000000
Checked arithmetic returns std::optional - containing the result on success, or std::nullopt on overflow.
This provides exception-free error handling with a clean, idiomatic interface.
link:example$checked_arithmetic.cpp[role=include]Output:
checked_add(max, 100) = overflow detected! checked_sub(10, 100) = underflow detected! checked_div(100, 0) = division by zero! checked_add(100, 50) = 150 checked_sub(100, 50) = 50 checked_mul(100, 50) = 5000 Safe: 1000000000 * 5 = 5000000000
Strict arithmetic calls std::exit(EXIT_FAILURE) on overflow, underflow, or division by zero.
This provides a hard termination policy for safety-critical applications where exceptions cannot be used but silent wrapping is unacceptable.
All strict functions are noexcept.
link:example$strict_arithmetic.cpp[role=include]Output:
strict_add(100, 50) = 150 strict_sub(100, 50) = 50 strict_mul(100, 50) = 5000 strict_div(100, 50) = 2 strict_mod(100, 50) = 0 strict_div(1000000, 3) = 333333 strict_mod(1000000, 3) = 1
The generic add, sub, mul, div, and mod functions accept an overflow_policy as a template parameter, allowing you to write code that is generic over the overflow handling strategy.
The return type varies by policy.
link:example$generic_arithmetic.cpp[role=include]Output:
add<throw_exception>(100, 50) = 150 add<saturate>(100, 50) = 150 add<strict>(100, 50) = 150 add<overflow_tuple>(100, 50) = 150 (overflowed: false) add<checked>(100, 50) = 150 add<checked>(max, 1) = nullopt (overflow) add<saturate>(max, 1) = 4294967295
User-defined literal suffixes (_u8, _u16, _u32, _u64, _u128) provide concise construction of safe integer types.
Values are range-checked at construction, producing a compile error in constexpr contexts or throwing std::overflow_error at runtime for out-of-range values.
link:example$literals.cpp[role=include]Output:
42_u8 = 42 1000_u16 = 1000 100000_u32 = 100000 9999999999_u64 = 9999999999 max_u128 = 340282366920938463463374607431768211455 100_u32 + 50_u32 = 150 6_u32 * 7_u32 = 42 constexpr 255_u8 = 255
The library provides to_chars and from_chars functions for converting between safe integers and strings using Boost.Charconv.
link:example$charconv.cpp[role=include]Output:
to_chars (base 10): 12345 to_chars (base 16): 3039 to_chars (base 2): 11000000111001 from_chars (base 10): 98765 from_chars (base 16): 6699 from_chars (base 2): 26
The library supports formatting with both <format> (C++20) and <fmt/format.h>.
All standard integer format specifiers are supported.
|
Important
|
The header <boost/safe_numbers/fmt_format.hpp> is NOT part of the convenience header, because it is an optional dependency.
|
link:example$fmt_format.cpp[role=include]Output:
Default Format:
12345
9876543210
Hexadecimal Format:
3039
0x24cb016ea
Binary Format:
11000000111001
0b101010
Octal Format:
30071
030071
Padding and Alignment:
12345
12345
12345
0000012345
Fill Character:
*****12345
12345_____
The library provides operator<< and operator>> overloads for all safe integer types.
The u8 type is handled specially: it displays as a numeric value rather than as a character.
link:example$iostream.cpp[role=include]Output:
u8: 42 u16: 1000 u32: 100000 u64: 9999999999 u8(10) = 10 (not a newline character) u8(32) = 32 (not a space character) Read from stream: 12345 Decimal: 255 Hexadecimal: ff Octal: 377 Roundtrip: 18446744073709551615 -> "18446744073709551615" -> 18446744073709551615
The library provides thin wrappers around all C++20 <bit> functions for safe integer types.
Each function extracts the underlying value, delegates to the standard library, and wraps the result back.
link:example$bit.cpp[role=include]Output:
has_single_bit(40) = 0 has_single_bit(32) = 1 bit_ceil(40) = 64 bit_floor(40) = 32 bit_width(40) = 6 rotl(0b10110001, 2) = 198 rotr(0b10110001, 2) = 108 countl_zero(0x0F00) = 4 countl_one(0x0F00) = 0 countr_zero(0x0F00) = 8 countr_one(0x0F00) = 0 popcount(0x0F00) = 4 byteswap(0x12345678) = 0x78563412
The library provides bitwise operators (~, &, |, ^, <<, >>) and their compound assignment forms (&=, |=, ^=, <⇐, >>=).
The NOT, AND, OR, and XOR operators are noexcept, while the shift operators throw std::overflow_error if bits would be shifted past the type width.
link:example$bitwise_ops.cpp[role=include]Output:
~a = 0xff00ff a & b = 0xf000f00 a | b = 0xff0fff0f a ^ b = 0xf00ff00f u8(1) << 4 = 16 u8(128) >> 4 = 8 x &= 0x0F0F -> 0xf00 x |= 0xF000 -> 0xff00 x ^= 0x00FF -> 0xffff y <<= 8 -> 256 y >>= 4 -> 16 Left shift error: Left shift past the end of the type width Right shift error: Right shift past the end of the type width saturating_shl(u8(1), 4) = 16 saturating_shl(u8(255), 1) = 255 saturating_shr(u8(128), 4) = 8 saturating_shr(u8(1), 8) = 0 overflowing_shl(u8(255), 1) = 254 (overflow: true) overflowing_shr(u8(1), 8) = 0 (overflow: true) checked_shl(u8(1), 4) = 16 checked_shl(u8(255), 1) = nullopt (overflow) checked_shr(u8(1), 8) = nullopt (overflow) shl<saturate>(u32(1), 30) = 1073741824 shl<saturate>(u32(max), 1) = 4294967295 shr<checked>(u32(8), 1) = 4
The library provides functions for converting safe integers to and from byte arrays in big-endian, little-endian, or native byte order.
link:example$byte_conversions.cpp[role=include]Output:
=== to_be_bytes ===
u32(0x01020304) -> BE bytes: 01 02 03 04
u16(0xABCD) -> BE bytes: ab cd
=== from_be_bytes ===
BE bytes {01,02,03,04} -> u32: 0x1020304
=== to_le_bytes ===
u32(0x01020304) -> LE bytes: 04 03 02 01
u64(0x01..08) -> LE bytes: 08 07 06 05 04 03 02 01
=== from_le_bytes ===
LE bytes {04,03,02,01} -> u32: 0x1020304
=== to_ne_bytes / from_ne_bytes (native endian) ===
u32(0xDEADBEEF) -> NE bytes: ef be ad de
Round-trip: -> u32: 0xdeadbeef
=== u8 round-trip ===
u8(0x42) -> BE: 42
u8(0x42) -> LE: 42
Safe integer types work seamlessly with standard library algorithms that require only comparison operators, such as std::min, std::max, std::clamp, std::sort, and std::minmax_element.
|
Note
|
std::gcd, std::lcm, and std::midpoint do not work because they static_assert on std::is_integral<T>, which is false for class types.
|
link:example$std_numeric_usage.cpp[role=include]Output:
=== std::min / std::max ===
std::min(42, 100) = 42
std::max(42, 100) = 100
std::min({30, 10, 50}) = 10
std::max({30, 10, 50}) = 50
=== std::clamp ===
std::clamp(5, 10, 200) = 10
std::clamp(50, 10, 200) = 50
std::clamp(999, 10, 200) = 200
=== std::sort ===
sorted: 10 20 30 40 50
=== std::minmax_element ===
min element = 10
max element = 50
=== Other widths ===
std::min(u8(10), u8(20)) = 10
std::max(u16(100), u16(200)) = 200
std::clamp(u64(500), u64(0), u64(100)) = 100
The library integrates with Boost.Random by providing a uniform_int_distribution specialization for all safe integer types.
This supports the full distribution interface including param_type, stream I/O, and range-constrained generation.
link:example$random.cpp[role=include]Output:
u32 in [1, 100]: 76 i32 in [-50, 50]: 14 u64 full range: 13874630024467741450 u16 in [0, 10]: 1 u128 in [0, 1000]: 904
All safe_numbers types and free functions are annotated with __host__ device, so they work identically on both host and device.
This example exercises arithmetic, bit functions, integer utilities, numeric algorithms, and charconv round-trips on a CUDA device using managed memory.
link:example$cuda.cu[role=include]Output:
Arithmetic (add): PASSED Bit (popcount): PASSED Utility (isqrt): PASSED Numeric (gcd): PASSED Charconv (rt): PASSED
When a safe_numbers operation overflows on the GPU, the error is captured in managed memory and rethrown with BOOST_THROW_EXCEPTION on the host when you call ctx.synchronize().
The device_error_context manages a dynamically allocated managed memory buffer; after catching an exception the same context can be reused immediately for new kernel launches.
device_error_context.link:example$cuda_error_handling.cu[role=include]Output:
=== Launching kernel that overflows === Caught overflow_error: Device error on thread 0 at /home/mborland/Documents/boost/libs/safe_numbers/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp:558: Overflow detected in u32 addition === Launching kernel with valid arithmetic === No error detected (expected) result[0] = 11 result[1] = 21 result[2] = 31 result[3] = 41
This example demonstrates what happens when a safe_numbers overflow occurs on a CUDA device without using device_error_context.
The overflow triggers a device-side trap that corrupts the CUDA context, making cudaDeviceSynchronize() report an unspecified launch failure.
Even after calling cudaDeviceReset(), the process cannot launch any further kernels — all subsequent CUDA operations fail.
This motivates the use of device_error_context shown in the previous example.
device_error_context for GPU error handling.link:example$cuda_error_handling_without_error_context.cu[role=include]Output:
=== Launching kernel that overflows === Kernel failed with error: unspecified launch failure Kernel has been reset via CUDA API === Launching kernel with valid arithmetic === Kernel failed with error: CUDA-capable device(s) is/are busy or unavailable