Overflow Policies

Description

The library provides multiple overflow handling policies for arithmetic operations. The default arithmetic operators (+, -, *, /, %) use the throw_exception policy, but alternative free functions and a generic policy-parameterized interface allow selecting different behavior at the call site.

The overflow_policy Enum

#include <boost/safe_numbers/overflow_policy.hpp>

namespace boost::safe_numbers {

enum class overflow_policy
{
    throw_exception, // Throw an exception on overflow/underflow
    saturate,        // Clamp to the representable range
    overflow_tuple,  // Wrap and return a flag indicating overflow
    checked,         // Return std::nullopt on overflow/underflow
    wrapping,        // Wrap silently
    strict,          // Call std::exit(EXIT_FAILURE) on error
};

} // namespace boost::safe_numbers

Policy Summary

Policy Overflow/Underflow Behavior Division by Zero noexcept

throw_exception (default)

Throws exception

Throws std::domain_error

No

saturate

Clamps to min/max

Throws std::domain_error

Add/Sub/Mul: Yes, Div/Mod: No

overflow_tuple

Wraps, returns flag

Throws std::domain_error

Add/Sub/Mul: Yes, Div/Mod: No

checked

Returns std::nullopt

Returns std::nullopt

Yes

wrapping

Wraps silently

Throws std::domain_error

Add/Sub/Mul: Yes, Div/Mod: No

strict

Calls std::exit(EXIT_FAILURE)

Calls std::exit(EXIT_FAILURE)

Yes

Named Arithmetic Functions

For cases where throwing exceptions is not desired, named free functions are provided for each policy.

Saturating Arithmetic

template <UnsignedLibType T>
constexpr T saturating_add(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr T saturating_sub(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr T saturating_mul(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr T saturating_div(T lhs, T rhs);

template <UnsignedLibType T>
constexpr T saturating_mod(T lhs, T rhs);

These functions clamp the result to the representable range instead of throwing:

  • saturating_add: Returns the sum, saturating at std::numeric_limits<T>::max() on overflow

  • saturating_sub: Returns the difference, saturating at std::numeric_limits<T>::min() (zero) on underflow

  • saturating_mul: Returns the product, saturating at std::numeric_limits<T>::max() on overflow

  • saturating_div: Returns the quotient; throws std::domain_error on division by zero (overflow is impossible)

  • saturating_mod: Returns the remainder; throws std::domain_error on division by zero (overflow is impossible)

Overflowing Arithmetic

template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_add(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_sub(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_mul(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_div(T lhs, T rhs);

template <UnsignedLibType T>
constexpr std::pair<T, bool> overflowing_mod(T lhs, T rhs);

These functions provide well-defined wrapping semantics with a flag to indicate if overflow occurred. This follows normal C family unsigned rollover where UINT_MAX + 1 == 0 and 0 - 1 == UINT_MAX.

  • overflowing_add: Returns the wrapped sum and true if overflow occurred

  • overflowing_sub: Returns the wrapped difference and true if underflow occurred

  • overflowing_mul: Returns the wrapped product and true if overflow occurred

  • overflowing_div: Returns the quotient and false; throws std::domain_error on division by zero

  • overflowing_mod: Returns the remainder and false; throws std::domain_error on division by zero

Checked Arithmetic

template <UnsignedLibType T>
constexpr std::optional<T> checked_add(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr std::optional<T> checked_sub(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr std::optional<T> checked_mul(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr std::optional<T> checked_div(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr std::optional<T> checked_mod(T lhs, T rhs) noexcept;

These functions return std::nullopt on overflow, underflow, or division by zero:

  • checked_add: Returns the sum, or std::nullopt on overflow

  • checked_sub: Returns the difference, or std::nullopt on underflow

  • checked_mul: Returns the product, or std::nullopt on overflow

  • checked_div: Returns the quotient, or std::nullopt on division by zero

  • checked_mod: Returns the remainder, or std::nullopt on division by zero

Wrapping Arithmetic

template <UnsignedLibType T>
constexpr T wrapping_add(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr T wrapping_sub(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr T wrapping_mul(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr T wrapping_div(T lhs, T rhs);

template <UnsignedLibType T>
constexpr T wrapping_mod(T lhs, T rhs);

These functions wrap on overflow without any indication:

  • wrapping_add: Returns the wrapped sum

  • wrapping_sub: Returns the wrapped difference

  • wrapping_mul: Returns the wrapped product

  • wrapping_div: Returns the quotient; throws std::domain_error on division by zero

  • wrapping_mod: Returns the remainder; throws std::domain_error on division by zero

Strict Arithmetic

template <UnsignedLibType T>
constexpr T strict_add(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr T strict_sub(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr T strict_mul(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr T strict_div(T lhs, T rhs) noexcept;

template <UnsignedLibType T>
constexpr T strict_mod(T lhs, T rhs) noexcept;

These functions call std::exit(EXIT_FAILURE) on error, providing a hard termination policy for safety-critical applications where exceptions cannot be used:

  • strict_add: Returns the sum; calls std::exit(EXIT_FAILURE) on overflow

  • strict_sub: Returns the difference; calls std::exit(EXIT_FAILURE) on underflow

  • strict_mul: Returns the product; calls std::exit(EXIT_FAILURE) on overflow

  • strict_div: Returns the quotient; calls std::exit(EXIT_FAILURE) on division by zero

  • strict_mod: Returns the remainder; calls std::exit(EXIT_FAILURE) on modulo by zero

All strict functions are marked noexcept since std::exit does not throw.

Generic Policy-Parameterized Arithmetic

template <overflow_policy Policy, UnsignedLibType T>
constexpr auto add(T lhs, T rhs);

template <overflow_policy Policy, UnsignedLibType T>
constexpr auto sub(T lhs, T rhs);

template <overflow_policy Policy, UnsignedLibType T>
constexpr auto mul(T lhs, T rhs);

template <overflow_policy Policy, UnsignedLibType T>
constexpr auto div(T lhs, T rhs);

template <overflow_policy Policy, UnsignedLibType T>
constexpr auto mod(T lhs, T rhs);

These functions accept an overflow_policy as a template parameter and dispatch to the corresponding named function. The return type depends on the policy:

Policy Return Type

overflow_policy::throw_exception

T

overflow_policy::saturate

T

overflow_policy::overflow_tuple

std::pair<T, bool>

overflow_policy::checked

std::optional<T>

overflow_policy::wrapping

T

overflow_policy::strict

T

This allows writing generic code parameterized on the overflow policy:

using namespace boost::safe_numbers;

// The policy can be a template parameter of your own function
template <overflow_policy Policy>
auto compute(u32 a, u32 b)
{
    return add<Policy>(a, b);
}

auto result_sat = compute<overflow_policy::saturate>(u32{100}, u32{200});
auto result_chk = compute<overflow_policy::checked>(u32{100}, u32{200});

Exception Summary

The default operators and some named functions throw exceptions on error:

Operation Exception Type Condition

+, +=

std::overflow_error

Result exceeds maximum value

-, -=

std::underflow_error

Result would be negative

*, *=

std::overflow_error

Result exceeds maximum value

/, /=

std::domain_error

Division by zero

%, %=

std::domain_error

Modulo by zero

++ (pre/post)

std::overflow_error

Value is at maximum

-- (pre/post)

std::underflow_error

Value is zero

saturating_div, saturating_mod

std::domain_error

Division by zero

overflowing_div, overflowing_mod

std::domain_error

Division by zero

wrapping_div, wrapping_mod

std::domain_error

Division by zero