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;
}
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;
}
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;
}
//]
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;
}
//]
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;
}
//]
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;
}
//]
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
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 |