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
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.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//[strict_arithmetic_example
//` This example demonstrates the use of strict arithmetic operations.
//` These functions call std::exit(EXIT_FAILURE) on overflow, underflow,
//` or division by zero. They are designed for safety-critical applications
//` where exceptions cannot be used but silent wrapping is unacceptable.
#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <cstdint>
int main()
{
using boost::safe_numbers::u32;
using boost::safe_numbers::strict_add;
using boost::safe_numbers::strict_sub;
using boost::safe_numbers::strict_mul;
using boost::safe_numbers::strict_div;
using boost::safe_numbers::strict_mod;
// Normal operations that don't overflow work as expected
{
const u32 a {100U};
const u32 b {50U};
std::cout << "strict_add(100, 50) = " << strict_add(a, b) << std::endl;
std::cout << "strict_sub(100, 50) = " << strict_sub(a, b) << std::endl;
std::cout << "strict_mul(100, 50) = " << strict_mul(a, b) << std::endl;
std::cout << "strict_div(100, 50) = " << strict_div(a, b) << std::endl;
std::cout << "strict_mod(100, 50) = " << strict_mod(a, b) << std::endl;
}
// Strict arithmetic is noexcept - safe for -fno-exceptions environments
{
const u32 a {1000000U};
const u32 b {3U};
// These are guaranteed to never throw
static_assert(noexcept(strict_add(a, b)));
static_assert(noexcept(strict_sub(a, b)));
static_assert(noexcept(strict_mul(a, b)));
static_assert(noexcept(strict_div(a, b)));
static_assert(noexcept(strict_mod(a, b)));
std::cout << "strict_div(1000000, 3) = " << strict_div(a, b) << std::endl;
std::cout << "strict_mod(1000000, 3) = " << strict_mod(a, b) << std::endl;
}
// NOTE: If any of these operations would overflow, underflow, or
// divide by zero, the program would immediately terminate via
// std::exit(EXIT_FAILURE). For example:
//
// u32 max_val {std::numeric_limits<std::uint32_t>::max()};
// strict_add(max_val, u32{1U}); // Terminates the program
//
// strict_sub(u32{0U}, u32{1U}); // Terminates the program
//
// strict_div(u32{1U}, u32{0U}); // Terminates the program
return 0;
}
//]
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.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//[generic_arithmetic_example
//` This example demonstrates the generic policy-parameterized arithmetic
//` functions (add, sub, mul, div, mod). These accept an overflow_policy
//` as a template parameter, allowing you to write code that is generic
//` over the overflow handling strategy.
#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/overflow_policy.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <cstdint>
#include <limits>
#include <optional>
#include <utility>
int main()
{
using boost::safe_numbers::u32;
using boost::safe_numbers::overflow_policy;
using boost::safe_numbers::add;
using boost::safe_numbers::sub;
using boost::safe_numbers::mul;
const u32 a {100U};
const u32 b {50U};
// Same operation, different policies via the generic interface
{
const auto throwing {add<overflow_policy::throw_exception>(a, b)};
const auto saturated {add<overflow_policy::saturate>(a, b)};
const auto wrapped {add<overflow_policy::wrapping>(a, b)};
const auto strict {add<overflow_policy::strict>(a, b)};
std::cout << "add<throw_exception>(100, 50) = " << throwing << std::endl;
std::cout << "add<saturate>(100, 50) = " << saturated << std::endl;
std::cout << "add<wrapping>(100, 50) = " << wrapped << std::endl;
std::cout << "add<strict>(100, 50) = " << strict << std::endl;
}
// Policies with different return types
{
// overflow_tuple returns std::pair<T, bool>
const auto [result_ot, overflowed] {add<overflow_policy::overflow_tuple>(a, b)};
std::cout << "add<overflow_tuple>(100, 50) = " << result_ot
<< " (overflowed: " << std::boolalpha << overflowed << ")" << std::endl;
// checked returns std::optional<T>
const auto result_chk {add<overflow_policy::checked>(a, b)};
if (result_chk)
{
std::cout << "add<checked>(100, 50) = " << *result_chk << std::endl;
}
}
// The real power: writing generic algorithms parameterized on policy
{
const u32 max_val {std::numeric_limits<std::uint32_t>::max()};
const u32 one {1U};
// checked policy returns nullopt on overflow
const auto checked_result {add<overflow_policy::checked>(max_val, one)};
std::cout << "add<checked>(max, 1) = "
<< (checked_result ? "has value" : "nullopt (overflow)")
<< std::endl;
// saturate policy clamps to max
const auto sat_result {add<overflow_policy::saturate>(max_val, one)};
std::cout << "add<saturate>(max, 1) = " << sat_result << std::endl;
// wrapping policy wraps around
const auto wrap_result {add<overflow_policy::wrapping>(max_val, one)};
std::cout << "add<wrapping>(max, 1) = " << wrap_result << std::endl;
}
return 0;
}
//]
Output:
add<throw_exception>(100, 50) = 150 add<saturate>(100, 50) = 150 add<wrapping>(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 add<wrapping>(max, 1) = 0
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.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//[literals_example
//` This example demonstrates the use of user-defined literals for
//` constructing safe integer types. The literals provide a concise
//` syntax and perform compile-time range checking when possible.
#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/literals.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
int main()
{
using namespace boost::safe_numbers;
using namespace boost::safe_numbers::literals;
// Construct safe integers using literal suffixes
{
constexpr auto a {42_u8};
constexpr auto b {1000_u16};
constexpr auto c {100000_u32};
constexpr auto d {9999999999_u64};
constexpr auto e {340282366920938463463374607431768211455_u128};
std::cout << "42_u8 = " << a << std::endl;
std::cout << "1000_u16 = " << b << std::endl;
std::cout << "100000_u32 = " << c << std::endl;
std::cout << "9999999999_u64 = " << d << std::endl;
std::cout << "max_u128 = " << e << std::endl;
}
// Literals work naturally in expressions
{
const auto sum {100_u32 + 50_u32};
const auto product {6_u32 * 7_u32};
std::cout << "100_u32 + 50_u32 = " << sum << std::endl;
std::cout << "6_u32 * 7_u32 = " << product << std::endl;
}
// Literals are constexpr - can be used in compile-time contexts
{
constexpr auto compile_time {255_u8};
static_assert(compile_time == u8{255U});
constexpr auto zero {0_u32};
static_assert(zero == u32{0U});
std::cout << "constexpr 255_u8 = " << compile_time << std::endl;
}
// NOTE: Out-of-range literals throw std::overflow_error at runtime,
// or produce a compile error when used in constexpr context:
//
// auto bad = 256_u8; // throws std::overflow_error (> UINT8_MAX)
// auto bad = 70000_u16; // throws std::overflow_error (> UINT16_MAX)
return 0;
}
//]
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.
// 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_____
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.
// Copyright 2025 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//[iostream_example
//` This example demonstrates stream I/O support for safe integer types.
//` Safe integers can be written to and read from standard streams using
//` the familiar << and >> operators. The u8 type displays as a number
//` rather than as a character.
#include <boost/safe_numbers/unsigned_integers.hpp>
#include <boost/safe_numbers/iostream.hpp>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdint>
int main()
{
using namespace boost::safe_numbers;
// Output: safe integers work with std::cout
{
const u8 a {42U};
const u16 b {1000U};
const u32 c {100000U};
const u64 d {9999999999ULL};
std::cout << "u8: " << a << std::endl;
std::cout << "u16: " << b << std::endl;
std::cout << "u32: " << c << std::endl;
std::cout << "u64: " << d << std::endl;
}
// u8 displays as a number, not a character
{
const u8 newline {10U};
const u8 space {32U};
std::cout << "u8(10) = " << newline << " (not a newline character)" << std::endl;
std::cout << "u8(32) = " << space << " (not a space character)" << std::endl;
}
// Input: safe integers work with std::istringstream
{
u32 value;
std::istringstream input {"12345"};
input >> value;
std::cout << "Read from stream: " << value << std::endl;
}
// Works with stream formatting
{
const u32 val {255U};
std::cout << "Decimal: " << std::dec << val << std::endl;
std::cout << "Hexadecimal: " << std::hex << val << std::endl;
std::cout << "Octal: " << std::oct << val << std::endl;
std::cout << std::dec; // Reset to decimal
}
// Roundtrip through a stringstream
{
const u64 original {18446744073709551615ULL};
std::stringstream ss;
ss << original;
u64 restored;
ss >> restored;
std::cout << "Roundtrip: " << original << " -> \"" << original << "\" -> " << restored << std::endl;
}
return 0;
}
//]
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
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 |
Strict |
Calls |
|
Safety-critical, no-exceptions environments |
Generic ( |
Depends on policy |
Depends on policy |
Policy-generic algorithms |