Skip to main content

Boost.Format

boost::format gives you type-safe, positional string formatting using a printf-inspired syntax. Unlike printf, it catches type mismatches at run time instead of silently producing garbage, and unlike manual ostringstream chaining, it keeps the format string readable. It was the go-to formatting tool in C++ until std::format arrived in C++20.

The problem it solves

printf is concise but unsafe — pass a double where it expects int and you get undefined behaviour. ostringstream is safe but verbose — building a formatted message means chaining << with manipulators. boost::format combines the readability of a format string with the type safety of streams.

Basic usage

The format string uses %N% positional placeholders (one-indexed):

basic.cpp
#include <boost/format.hpp>
#include <iostream>
#include <string>

int main() {
std::string msg = (boost::format("Hello, %1%! You have %2% messages.")
% "Alice" % 42).str();
std::cout << msg << "\n";
// Hello, Alice! You have 42 messages.
}

Each % operator feeds the next argument; .str() extracts the result as a std::string. You can also stream directly:

std::cout << boost::format("(%1%, %2%)") % 3.14 % 2.72 << "\n";
// (3.14, 2.72)

Positional reuse

Because placeholders are numbered, you can reuse and reorder them:

positional.cpp
#include <boost/format.hpp>
#include <iostream>

int main() {
auto fmt = boost::format("%1% said: '%2%'. Yes, %1% really said that.")
% "Bob" % "hello";
std::cout << fmt << "\n";
// Bob said: 'hello'. Yes, Bob really said that.
}

This is impossible with printf and awkward with ostringstream.

Printf-style format specifiers

Boost.Format also accepts classic printf specifiers for width, precision, and fill:

specifiers.cpp
#include <boost/format.hpp>
#include <iostream>

int main() {
// Fixed-width columns
std::cout << boost::format("%-20s %10.2f\n") % "Widget" % 19.99;
std::cout << boost::format("%-20s %10.2f\n") % "Gadget" % 149.50;
// Widget 19.99
// Gadget 149.50

// Hex, zero-padded
std::cout << boost::format("0x%08x") % 255 << "\n";
// 0x000000ff
}
Mixing positional and printf-style placeholders

You can use %1% positional syntax or %s/%d printf syntax, but do not mix both in one format string. Pick one style per call.

Error handling

Boost.Format validates argument count and throws on mismatch:

errors.cpp
#include <boost/format.hpp>
#include <iostream>

int main() {
try {
// Too few arguments — throws on .str()
auto s = (boost::format("%1% and %2%") % "only-one").str();
} catch (const boost::io::too_few_args& e) {
std::cout << "Error: " << e.what() << "\n";
}
}
ExceptionCause
too_few_argsFewer % arguments than placeholders
too_many_argsMore % arguments than placeholders
bad_format_stringMalformed format specifier

Boost.Format versus std::format versus printf

Featureprintfboost::formatstd::format (C++20)
Type safenoyes (runtime)yes (compile-time)
Positional argsPOSIX extension (%1$s)%1%{0}
Extensible to user typesnoyes (via operator<<)yes (via std::formatter)
Compile-time checksnonoyes
Header<cstdio><boost/format.hpp><format>
Performancefastmoderatefast
Which to choose

On C++20 and later, prefer std::format — it is faster, catches errors at compile time, and needs no dependency. Boost.Format remains useful on pre-C++20 toolchains or when you need its %N% positional syntax in existing codebases. See Boost and the standard.

Reusable format objects

A boost::format object can be bound to a format string once and reused:

reuse.cpp
#include <boost/format.hpp>
#include <iostream>

int main() {
boost::format row("| %-15s | %6d | %8.2f |");
std::cout << (row % "Apples" % 120 % 1.50) << "\n";
row.clear(); // reset for reuse
std::cout << (row % "Bananas" % 45 % 0.75) << "\n";
}

Call .clear() between uses to reset the argument state without reparsing the format string.

See also