Skip to content

Latest commit

 

History

History
636 lines (521 loc) · 20.1 KB

File metadata and controls

636 lines (521 loc) · 20.1 KB

Examples

All the following examples can be found in the examples/ folder of the library.

Basic Usage (Default Throwing Behavior)

Example 1. This example demonstrates the default behavior where arithmetic overflow throws an exception.
link:example$basic_usage.cpp[role=include]

Output:

Error Detected: Overflow detected in unsigned addition

Basic Usage with Diagnostic Information

Example 2. This example demonstrates using 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

Basic Bounded Usage

Example 3. This example demonstrates the default behavior where bounded arithmetic that exceeds the declared range throws an exception.
link:example$basic_bounded_usage.cpp[role=include]

Output:

Error Detected: bounded_uint<0, 100> addition result out of range

Construction and Conversion

Example 4. This example demonstrates safe integer construction and conversion.
link:example$construction_and_conversion.cpp[role=include]

Output:

Safe Value: 42
Builtin Value: 42

Saturating Arithmetic

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.

Example 5. This example demonstrates saturating arithmetic operations.
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

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.

Example 6. This example demonstrates overflowing arithmetic operations.
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

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.

Example 7. This example demonstrates checked arithmetic operations.
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

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.

Example 8. This example demonstrates strict arithmetic operations.
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

Generic Policy-Parameterized Arithmetic

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.

Example 9. This example demonstrates the generic policy-parameterized interface.
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

Literals

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.

Example 10. This example demonstrates how to use user-defined literals with safe integer types.
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

Character Conversion

The library provides to_chars and from_chars functions for converting between safe integers and strings using Boost.Charconv.

Example 11. This example demonstrates character conversion with various bases.
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

Formatting with {fmt}

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.
Example 12. This example demonstrates formatting with {fmt}.
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_____

Stream I/O

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.

Example 13. This example demonstrates stream I/O with safe integer types.
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

Bit Manipulation

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.

Example 14. This example demonstrates the bit manipulation functions.
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

Bitwise Operations

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.

Example 15. This example demonstrates the bitwise operators and shift overflow detection.
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

Byte Conversions

The library provides functions for converting safe integers to and from byte arrays in big-endian, little-endian, or native byte order.

Example 16. This example demonstrates byte conversion functions with all safe integer types.
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

Standard Library Compatibility

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.
Example 17. This example demonstrates standard library algorithm compatibility.
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

Random Number Generation

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.

Example 18. This example demonstrates generating uniformly distributed safe integer values.
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

CUDA Device Support

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.

Example 19. This example demonstrates safe_numbers operations running on a CUDA device.
link:example$cuda.cu[role=include]

Output:

Arithmetic (add):  PASSED
Bit (popcount):    PASSED
Utility (isqrt):   PASSED
Numeric (gcd):     PASSED
Charconv (rt):     PASSED

CUDA Error Handling

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.

Example 20. This example demonstrates catching arithmetic errors that occur on a CUDA device using 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

CUDA Error Handling Without Error Context

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.

Example 21. This example demonstrates the consequences of not using 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