Stream I/O Support
Description
The library provides operator<< and operator>> overloads for all safe integer types, enabling use with standard C++ streams (std::cout, std::cin, std::stringstream, etc.).
The u8 type is handled specially: it displays as a numeric value rather than as a character, unlike std::uint8_t which most implementations treat as unsigned char.
Attempting to read a negative value into an unsigned safe integer throws std::domain_error.
#include <boost/safe_numbers/iostream.hpp>
namespace boost::safe_numbers::detail {
// Output: writes the numeric value to the stream
template <typename charT, typename traits, library_type LibType>
auto operator<<(std::basic_ostream<charT, traits>& os, const LibType& v)
-> std::basic_ostream<charT, traits>&;
// Input: reads a numeric value from the stream
// Throws std::domain_error if the input begins with '-'
template <typename charT, typename traits, library_type LibType>
auto operator>>(std::basic_istream<charT, traits>& is, LibType& v)
-> std::basic_istream<charT, traits>&;
} // namespace boost::safe_numbers::detail
Behavior
Output (operator<<)
| Type | Behavior |
|---|---|
|
Promoted to |
|
Converted to the underlying type and written directly |
|
Converted to the underlying |
Standard stream manipulators (std::hex, std::oct, std::dec, std::setw, etc.) work as expected.
Input (operator>>)
| Input | Behavior |
|---|---|
Valid non-negative number |
Parsed and stored in the safe integer |
Negative number (starts with |
Throws |
Invalid input |
Sets the stream’s fail bit as with any standard stream extraction |
For u8, the input is read as a std::uint32_t to avoid character interpretation, then narrowed to std::uint8_t.
Examples
// 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