Examples
All the following examples can be found in the examples/ folder of the library.
Basic Usage (Default Throwing Behavior)
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/safe_numbers/unsigned_integers.hpp> // For safe_numbers types
#include <boost/safe_numbers/iostream.hpp> // For safe_numbers <iostream> support
#include <iostream>
#include <cstdint>
int main()
{
using boost::safe_numbers::u8;
try
{
const u8 x {UINT8_MAX};
const u8 y {2};
const u8 z {x + y};
std::cout << "Value of z: " << z << std::endl;
}
catch (const std::exception& e)
{
std::cerr << "Error Detected: " << e.what() << std::endl;
}
return 0;
}
Output:
Error Detected: Overflow detected in unsigned addition
Construction and Conversion
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//
// This example demonstrates how to construct a safe unsigned integer,
// and then convert it back to built-in types
#include <boost/safe_numbers/unsigned_integers.hpp> // For the safe unsigned integers
#include <boost/safe_numbers/iostream.hpp> // For safe numbers support to <iostream>
#include <iostream>
#include <cstdint>
int main()
{
using boost::safe_numbers::u32; // 32-bit unsigned safe integer
// Construct a safe u32 from a builtin unsigned value explicitly
constexpr u32 safe_value {42U};
// Safe values support <iostream> in the same way that the builtins do
std::cout << "Safe Value: " << safe_value << '\n';
// To convert the safe value back to a builtin value
// use the known basis, or generically via the basis_type typedef
constexpr std::uint32_t unsafe_value {static_cast<std::uint32_t>(safe_value)};
constexpr std::uint32_t unsafe_value_from_typedef {static_cast<u32::basis_type>(safe_value)};
// The conversions are all completely constexpr
// Invalid conversions such as narrowing will result in compile errors
// instead of run-time error
if constexpr (unsafe_value == unsafe_value_from_typedef)
{
std::cout << "Builtin Value: " << unsafe_value << '\n';
}
return 0;
}
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.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//[saturating_arithmetic_example
//` This example demonstrates the use of saturating arithmetic operations.
//` When overflow or underflow would occur, the result saturates at the
//` type's maximum or minimum value instead of wrapping or throwing.
#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <cstdint>
#include <limits>
int main()
{
using boost::safe_numbers::u32;
using boost::safe_numbers::saturating_add;
using boost::safe_numbers::saturating_sub;
using boost::safe_numbers::saturating_mul;
// Saturating addition: clamps to max on overflow
{
const u32 a {std::numeric_limits<std::uint32_t>::max()};
const u32 b {100U};
const u32 result {saturating_add(a, b)};
std::cout << "saturating_add(max, 100) = " << result << std::endl;
// Output: 4294967295 (UINT32_MAX, saturated)
}
// Saturating subtraction: clamps to min (0) on underflow
{
const u32 a {10U};
const u32 b {100U};
const u32 result {saturating_sub(a, b)};
std::cout << "saturating_sub(10, 100) = " << result << std::endl;
// Output: 0 (saturated at minimum)
}
// Saturating multiplication: clamps to max on overflow
{
const u32 a {std::numeric_limits<std::uint32_t>::max()};
const u32 b {2U};
const u32 result {saturating_mul(a, b)};
std::cout << "saturating_mul(max, 2) = " << result << std::endl;
// Output: 4294967295 (UINT32_MAX, saturated)
}
// Normal operations that don't overflow work as expected
{
const u32 a {100U};
const u32 b {50U};
std::cout << "saturating_add(100, 50) = " << saturating_add(a, b) << std::endl;
std::cout << "saturating_sub(100, 50) = " << saturating_sub(a, b) << std::endl;
std::cout << "saturating_mul(100, 50) = " << saturating_mul(a, b) << std::endl;
// Output: 150, 50, 5000
}
return 0;
}
//]
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.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//[overflowing_arithmetic_example
//` This example demonstrates the use of overflowing arithmetic operations.
//` These functions return a std::pair containing the result and a boolean
//` flag indicating whether overflow/underflow occurred. The result is the
//` wrapped value (as if using normal unsigned arithmetic).
#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <cstdint>
#include <limits>
#include <utility>
int main()
{
using boost::safe_numbers::u32;
using boost::safe_numbers::overflowing_add;
using boost::safe_numbers::overflowing_sub;
using boost::safe_numbers::overflowing_mul;
// Overflowing addition: returns (wrapped_result, did_overflow)
{
const u32 a {std::numeric_limits<std::uint32_t>::max()};
const u32 b {100U};
const auto [result, overflowed] {overflowing_add(a, b)};
std::cout << "overflowing_add(max, 100):" << std::endl;
std::cout << " result = " << result << std::endl;
std::cout << " overflowed = " << std::boolalpha << overflowed << std::endl;
// Output: result = 99 (wrapped), overflowed = true
}
// Overflowing subtraction: returns (wrapped_result, did_underflow)
{
const u32 a {10U};
const u32 b {100U};
const auto [result, underflowed] {overflowing_sub(a, b)};
std::cout << "overflowing_sub(10, 100):" << std::endl;
std::cout << " result = " << result << std::endl;
std::cout << " underflowed = " << std::boolalpha << underflowed << std::endl;
// Output: result = 4294967206 (wrapped), underflowed = true
}
// Overflowing multiplication: returns (wrapped_result, did_overflow)
{
const u32 a {std::numeric_limits<std::uint32_t>::max()};
const u32 b {2U};
const auto [result, overflowed] {overflowing_mul(a, b)};
std::cout << "overflowing_mul(max, 2):" << std::endl;
std::cout << " result = " << result << std::endl;
std::cout << " overflowed = " << std::boolalpha << overflowed << std::endl;
// Output: result = 4294967294 (wrapped), overflowed = true
}
// Normal operations that don't overflow
{
const u32 a {100U};
const u32 b {50U};
const auto [add_result, add_overflow] {overflowing_add(a, b)};
const auto [sub_result, sub_overflow] {overflowing_sub(a, b)};
const auto [mul_result, mul_overflow] {overflowing_mul(a, b)};
std::cout << "overflowing_add(100, 50) = " << add_result
<< " (overflow: " << add_overflow << ")" << std::endl;
std::cout << "overflowing_sub(100, 50) = " << sub_result
<< " (overflow: " << sub_overflow << ")" << std::endl;
std::cout << "overflowing_mul(100, 50) = " << mul_result
<< " (overflow: " << mul_overflow << ")" << std::endl;
// Output: 150/false, 50/false, 5000/false
}
// Using the overflow flag for conditional logic
{
const u32 a {1000000000U};
const u32 b {5U};
if (const auto [result, overflowed] {overflowing_mul(a, b)}; !overflowed)
{
std::cout << "Safe multiplication: " << a << " * " << b << " = " << result << std::endl;
}
else
{
std::cout << "Multiplication would overflow!" << std::endl;
}
}
return 0;
}
//]
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.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//[checked_arithmetic_example
//` This example demonstrates the use of checked arithmetic operations.
//` These functions return std::optional - containing the result if the
//` operation succeeded, or std::nullopt if overflow/underflow occurred.
//` This provides a clean, exception-free way to handle arithmetic errors.
#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <cstdint>
#include <limits>
#include <optional>
int main()
{
using boost::safe_numbers::u32;
using boost::safe_numbers::checked_add;
using boost::safe_numbers::checked_sub;
using boost::safe_numbers::checked_mul;
using boost::safe_numbers::checked_div;
// Checked addition: returns nullopt on overflow
{
const u32 a {std::numeric_limits<std::uint32_t>::max()};
const u32 b {100U};
const auto result {checked_add(a, b)};
if (result.has_value())
{
std::cout << "checked_add(max, 100) = " << *result << std::endl;
}
else
{
std::cout << "checked_add(max, 100) = overflow detected!" << std::endl;
}
// Output: overflow detected!
}
// Checked subtraction: returns nullopt on underflow
{
const u32 a {10U};
const u32 b {100U};
const auto result {checked_sub(a, b)};
if (result.has_value())
{
std::cout << "checked_sub(10, 100) = " << *result << std::endl;
}
else
{
std::cout << "checked_sub(10, 100) = underflow detected!" << std::endl;
}
// Output: underflow detected!
}
// Checked division: returns nullopt on divide by zero
{
const u32 a {100U};
const u32 b {0U};
const auto result {checked_div(a, b)};
if (result.has_value())
{
std::cout << "checked_div(100, 0) = " << *result << std::endl;
}
else
{
std::cout << "checked_div(100, 0) = division by zero!" << std::endl;
}
// Output: division by zero!
}
// Successful operations return the value wrapped in optional
{
const u32 a {100U};
const u32 b {50U};
// Using value_or for safe access with default
std::cout << "checked_add(100, 50) = "
<< checked_add(a, b).value_or(u32{0U}) << std::endl;
std::cout << "checked_sub(100, 50) = "
<< checked_sub(a, b).value_or(u32{0U}) << std::endl;
std::cout << "checked_mul(100, 50) = "
<< checked_mul(a, b).value_or(u32{0U}) << std::endl;
// Output: 150, 50, 5000
}
// Chaining checked operations with value_or
{
const u32 a {1000000000U};
const u32 b {5U};
// Only proceed if multiplication doesn't overflow
const auto product {checked_mul(a, b)};
if (product)
{
std::cout << "Safe: " << a << " * " << b << " = " << *product << std::endl;
}
else
{
std::cout << "Operation would overflow, using fallback" << std::endl;
}
}
return 0;
}
//]
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
Wrapping Arithmetic
Wrapping arithmetic performs standard C unsigned integer wrapping behavior - results wrap around modulo 2^N. This matches the behavior of built-in unsigned integers and is useful for implementing counters, checksums, or hash functions.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//[wrapping_arithmetic_example
//` This example demonstrates the use of wrapping arithmetic operations.
//` These functions perform standard C unsigned integer wrapping behavior -
//` when overflow or underflow occurs, the result wraps around modulo 2^N.
//` This matches the behavior of built-in unsigned integers in C/C++.
#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <cstdint>
#include <limits>
int main()
{
using boost::safe_numbers::u8;
using boost::safe_numbers::u32;
using boost::safe_numbers::wrapping_add;
using boost::safe_numbers::wrapping_sub;
using boost::safe_numbers::wrapping_mul;
// Wrapping addition: wraps around on overflow
{
const u8 a {255U}; // UINT8_MAX
const u8 b {2U};
const u8 result {wrapping_add(a, b)};
std::cout << "wrapping_add(255, 2) = " << result << std::endl;
// Output: 1 (255 + 2 = 257, wraps to 257 % 256 = 1)
}
// Wrapping subtraction: wraps around on underflow
{
const u8 a {0U};
const u8 b {1U};
const u8 result {wrapping_sub(a, b)};
std::cout << "wrapping_sub(0, 1) = " << result << std::endl;
// Output: 255 (wraps to UINT8_MAX)
}
// Wrapping multiplication: wraps around on overflow
{
const u8 a {200U};
const u8 b {2U};
const u8 result {wrapping_mul(a, b)};
std::cout << "wrapping_mul(200, 2) = " << result << std::endl;
// Output: 144 (200 * 2 = 400, wraps to 400 % 256 = 144)
}
// Demonstration with u32
{
const u32 max_val {std::numeric_limits<std::uint32_t>::max()};
const u32 one {1U};
std::cout << "wrapping_add(UINT32_MAX, 1) = "
<< wrapping_add(max_val, one) << std::endl;
// Output: 0 (wraps around)
const u32 zero {0U};
std::cout << "wrapping_sub(0, 1) = "
<< wrapping_sub(zero, one) << std::endl;
// Output: 4294967295 (UINT32_MAX)
}
// Normal operations that don't overflow work as expected
{
const u32 a {100U};
const u32 b {50U};
std::cout << "wrapping_add(100, 50) = " << wrapping_add(a, b) << std::endl;
std::cout << "wrapping_sub(100, 50) = " << wrapping_sub(a, b) << std::endl;
std::cout << "wrapping_mul(100, 50) = " << wrapping_mul(a, b) << std::endl;
// Output: 150, 50, 5000
}
// Useful for implementing counters that wrap
{
u8 counter {254U};
std::cout << "Counter sequence: ";
for (int i = 0; i < 5; ++i)
{
std::cout << counter << " ";
counter = wrapping_add(counter, u8{1U});
}
std::cout << std::endl;
// Output: 254 255 0 1 2
}
return 0;
}
//]
Output:
wrapping_add(255, 2) = 1 wrapping_sub(0, 1) = 255 wrapping_mul(200, 2) = 144 wrapping_add(UINT32_MAX, 1) = 0 wrapping_sub(0, 1) = 4294967295 wrapping_add(100, 50) = 150 wrapping_sub(100, 50) = 50 wrapping_mul(100, 50) = 5000 Counter sequence: 254 255 0 1 2
Character Conversion
The library provides to_chars and from_chars functions for converting between safe integers and strings using Boost.Charconv.
// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/charconv.hpp>
#include <iostream>
#include <cstring>
int main()
{
using namespace boost::safe_numbers;
// to_chars: Convert safe integer to string
u32 value {12345};
char buffer[32];
auto result = to_chars(buffer, buffer + sizeof(buffer), value);
if (result)
{
*result.ptr = '\0'; // Null-terminate
std::cout << "to_chars (base 10): " << buffer << '\n';
}
// to_chars with different bases
result = to_chars(buffer, buffer + sizeof(buffer), value, 16);
if (result)
{
*result.ptr = '\0';
std::cout << "to_chars (base 16): " << buffer << '\n';
}
result = to_chars(buffer, buffer + sizeof(buffer), value, 2);
if (result)
{
*result.ptr = '\0';
std::cout << "to_chars (base 2): " << buffer << '\n';
}
std::cout << '\n';
// from_chars: Convert string to safe integer
const char* str = "98765";
u32 parsed_value {};
auto parse_result = from_chars(str, str + std::strlen(str), parsed_value);
if (parse_result)
{
std::cout << "from_chars (base 10): " << static_cast<std::uint32_t>(parsed_value) << '\n';
}
// from_chars with hexadecimal
const char* hex_str = "1a2b";
u32 hex_value {};
parse_result = from_chars(hex_str, hex_str + std::strlen(hex_str), hex_value, 16);
if (parse_result)
{
std::cout << "from_chars (base 16): " << static_cast<std::uint32_t>(hex_value) << '\n';
}
// from_chars with binary
const char* bin_str = "11010";
u8 bin_value {};
parse_result = from_chars(bin_str, bin_str + std::strlen(bin_str), bin_value, 2);
if (parse_result)
{
std::cout << "from_chars (base 2): " << static_cast<unsigned>(bin_value) << '\n';
}
return 0;
}
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.
The header <boost/safe_numbers/fmt_format.hpp> is NOT part of the convenience header, because it is an optional dependency.
|
// Copyright 2026 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#define FMT_HEADER_ONLY
#if __has_include(<fmt/format.h>)
#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/fmt_format.hpp>
#include <fmt/format.h>
#include <iostream>
int main()
{
using namespace boost::safe_numbers;
const u32 val1 {12345};
const u64 val2 {9876543210};
// Default format (decimal)
std::cout << "Default Format:\n";
std::cout << fmt::format("{}", val1) << '\n';
std::cout << fmt::format("{}", val2) << "\n\n";
// Hexadecimal format
std::cout << "Hexadecimal Format:\n";
std::cout << fmt::format("{:x}", val1) << '\n';
std::cout << fmt::format("{:#x}", val2) << "\n\n";
// Binary format
std::cout << "Binary Format:\n";
std::cout << fmt::format("{:b}", val1) << '\n';
std::cout << fmt::format("{:#b}", u8{42}) << "\n\n";
// Octal format
std::cout << "Octal Format:\n";
std::cout << fmt::format("{:o}", val1) << '\n';
std::cout << fmt::format("{:#o}", val1) << "\n\n";
// Padding and alignment
std::cout << "Padding and Alignment:\n";
std::cout << fmt::format("{:>10}", val1) << '\n'; // Right align
std::cout << fmt::format("{:<10}", val1) << '\n'; // Left align
std::cout << fmt::format("{:^10}", val1) << '\n'; // Center align
std::cout << fmt::format("{:0>10}", val1) << "\n\n"; // Zero-padded
// Fill character
std::cout << "Fill Character:\n";
std::cout << fmt::format("{:*>10}", val1) << '\n';
std::cout << fmt::format("{:_<10}", val1) << '\n';
return 0;
}
#else
#include <iostream>
int main()
{
std::cout << "{fmt} headers are required to run this example." << std::endl;
}
#endif
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_____
Policy Comparison
The following table summarizes the behavior of each arithmetic policy:
| Policy | On Overflow | Return Type | Use Case |
|---|---|---|---|
Default (operators) |
Throws |
|
When overflow is a programming error |
Saturating |
Clamps to min/max |
|
Bounded values, DSP, graphics |
Overflowing |
Returns wrapped value + flag |
|
Need both wrapped value and detection |
Checked |
Returns |
|
Exception-free error handling |
Wrapping |
Wraps around (modulo 2^N) |
|
Counters, checksums, hashing |