Formatting Support
Description
Boost.SafeNumbers supports formatting with both <format> (C++20 and later) and <fmt/format.h> (all language standards).
The formatters delegate to the underlying integer type’s formatter, so all standard integer format specifiers are supported.
Both safe unsigned integers (u8, u16, u32, u64, u128) and bounded integers (bounded_uint<Min, Max>) are supported.
std::format is supported when using C++20 or later with a compiler that has appropriate support: GCC >= 13, Clang >= 18, MSVC >= 19.40.
#include <boost/safe_numbers/format.hpp> // For std::format support
#include <boost/safe_numbers/fmt_format.hpp> // For fmt::format support
Synopsis
// <boost/safe_numbers/format.hpp>
template <detail::library_type T>
struct std::formatter<T>
: std::formatter<detail::underlying_type_t<T>>
{
auto format(const T& val, std::format_context& ctx) const;
};
// <boost/safe_numbers/fmt_format.hpp>
template <detail::library_type T>
struct fmt::formatter<T>
: fmt::formatter<detail::underlying_type_t<T>>
{
auto format(const T& val, fmt::format_context& ctx) const;
};
Type Modifiers
The following type modifiers are supported (same as built-in integer types):
| Modifier | Format | Example |
|---|---|---|
|
Decimal (default) |
|
|
Lowercase hexadecimal |
|
|
Uppercase hexadecimal |
|
|
Octal |
|
|
Lowercase binary |
|
|
Uppercase binary |
|
Example usage for hexadecimal format: {:x}
Alternate Form
Use # to enable alternate form which adds a prefix:
| Format | Prefix |
|---|---|
|
|
|
|
|
|
|
|
|
|
Padding and Alignment
Values can be padded to a fixed width with optional alignment:
| Specifier | Alignment | Example (val = 42) |
|---|---|---|
|
Right (default) |
` 42` |
|
Left |
`42 ` |
|
Center |
` 42 ` |
|
Right with zero fill |
|
Fill Character
A custom fill character can be specified before the alignment:
-
{:*>10}produces**42 -
{:_<10}produces42__
Sign Modifier
For unsigned types, sign modifiers have limited effect since values are always non-negative:
| Modifier | Effect |
|---|---|
|
Always show sign (shows |
|
Only show sign for negative (no effect for unsigned) |
` ` |
Space for positive, minus for negative |
Locale Modifier
Use L to apply locale-specific formatting (e.g., thousand separators):
std::locale::global(std::locale("en_US.UTF-8"));
std::cout << std::format("{:L}", u32{1234567}); // "1,234,567"
Format Specifier Order
The full format specifier order is:
{[fill][align][sign][#][0][width][.precision][L][type]}
Examples
The header <boost/safe_numbers/fmt_format.hpp> is NOT part of the convenience header, because it is an optional dependency on a potentially compiled library.
|
// 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/bounded_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\n";
// Bounded integer types
using percent = bounded_uint<0u, 100u>;
using port = bounded_uint<1u, 65535u>;
std::cout << "Bounded Integers:\n";
std::cout << fmt::format("{}", percent{75u}) << '\n';
std::cout << fmt::format("{:>8}", port{8080u}) << '\n';
std::cout << fmt::format("{:#x}", port{443u}) << '\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_____
Bounded Integers:
75
8080
0x1bb
This same example can be run with <format> by replacing fmt::format with std::format and including <boost/safe_numbers/format.hpp> instead.