str_format library is a suite of classes and functions that
collectively serve as a type-safe C++ implementation of C’s
str_format library provides the following benefits:
"%2$d") that would not be supported on the native
vsnprintfvariant in MSVC. Some useful conversions would produce unreliable output or fail silently on those platforms.
absl::StrFormat()is the same everywhere.
va_list’s distortion of argument boundaries, which can leak data.
str_formatlibrary allows those length modifiers but ignores them, so arguments can change type without affecting caller code.
std::ostreamsinks instead of requiring the use, for example, of temporary strings within wrapper functions.
dtoaalgorithms because we control the implementation.
vsnprintfare all written in C, and extremely hard to read.
Every effort was made to support or address the spec for POSIX printf, including facilities such as positional specifiers not found in standard C and C++.
A libc-based printf function can be extremely unsafe, since argument boundaries
are not preserved by
va_list. If the size of the type indicated by the format
string doesn’t match the size of the object placed into the
va_list by the
std::printf() can print data that is not meant to be printed, or print
std::printf("%llx %d %s", 1, 2, s)
could print some garbage from the stack, because the first argument isn’t wide
enough to fill its
Compile-time format string checking helps us out here but it cannot be relied on
for safety. There are very basic scenarios that it cannot validate. The
str_format library has the ability to evaluate arguments as exactly the types
they were passed as, without the dangers of C-style casting necessitated by the
C stdarg feature. If that type is convertible to the type expected by the
conversion specifier, we can convert safely. Otherwise, it can return an error.
It never has to guess at the size and layout of an input argument.
There are some cases where
StrFormat() behaves differently from the specified
std::printf(). Some cases that were undefined behavior under
std::printf() are omitted from the discussion.
'has no effect.
Implicit promotion of integral types
std::printf(), all small integral types are promoted to
unsigned on their way into the
va_list, retaining their signedness. They
are then demoted again by length modifiers (e.g.
%hu), hopefully to the
absl::StrFormat() will fully retain the argument type. When
printing a signed integer with an unsigned conversion, like
the value is not widened, but only converted to its unsigned equivalent,
preserving its bitwise value. Printing a signed number with an unsigned
format is dodgy in the first place. Formatting of negative values with an
unsigned conversion yields undefined behavior in
divergence from printf-style output here should be acceptable.
Implicit promotion of float to double
float argument is implicitly converted to
double by the C varargs
convention. This does not happen in
absl::StrFormat(), but as long as
float value can be held in a
double (this is the case), there
should be no noticeable effect.
The implementation of
absl::StrFormat() is modular and composed. Its
components are individually tested and reusable.
The implementation of
vfprintf.c (the engine for
usually pretty tough reading. In C, an implementation cannot depend on
inlining or template functions, so there’s a lot of repetition and gotos,
sometimes abstracted with macros.
The GLIBC implementation relies heavily on macros, and is a 2000-line function.
The FreeBSD implementation is much cleaner, but is more limited in functionality and still 700 lines long.
We have several systems that have a printf-like vararg API for logging. The
building blocks of
absl::StrFormat() are reusable in such contexts. A public
interface explicitly supports callers who have specialized
format strings, and data sinks.
As an extension,
absl::Format() can append to objects other than strings,
allowing them to use a domain-appropriate buffering or reallocation strategy as
the formatting progresses.
Simple hooks can be written to support efficient printing to any data sink.
str_format library contains a family of functions. While
absl::StrFormat() returns a string (and is analagous to
std::printf() type behavior and can return errors.
In debug builds, we can log or die when a format doesn’t exactly match its input
parameter types. Other variants can have better error handling. Variants of the
absl::StrFormat() entry points can be developed to produce better error
boost::formatlibrary, streams-based, not exactly POSIX compliant. Some fancy extensions, doesn’t support arg-specified width or precision. Also very slow at about 5x slower than raw streams.
SafeSprintf: Supports only int, uint, string, pointer. Part of Chromium. Ignores width specs. Async safe.
NOTE: “How to Print Floating-Point Numbers Accurately” by Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN ‘90, pp. 112-126].