The str_format library is a typesafe replacement for the family of printf()
string formatting routines within the <cstdio> standard library header. The
str_format library provides most of the functionality of printf() type
string formatting and a number of additional benefits:
std::string and
absl::string_viewabsl::Cord natively and can be extended to
support other types.printf functionsAdditionally, the library includes replacements for printf(), fprintf(), and
snprintf().
The main StrFormat() function is a variadic template that returns a string
given a printf()-style format string and zero or more additional arguments.
Use it as you would sprintf().
The format string generally consists of ordinary character data along with one
or more format conversion specifiers (denoted by the % character). Ordinary
character data is returned unchanged into the result string, while each
conversion specification performs a type substitution from StrFormat()’s other
arguments.
StrFormat() returns an empty string on error, and is marked as
ABSL_MUST_USE_RESULT.
Example:
#include "absl/strings/str_format.h"
std::string s = absl::StrFormat("Welcome to %s, Number %d!", "The Village", 6);
EXPECT_EQ("Welcome to The Village, Number 6!", s);
The StrFormat() format string should usually be declared constexpr*; as a
result, if you need to provide it as a variable, use an absl::string_view
instead of a std::string:
// Won't compile, not constexpr (and the `std::string` can't be declared
// constexpr).
std::string format_string = "Welcome to %s, Number %d!";
std::string s = absl::StrFormat(format_string, "The Village", 6);
// This will compile.
constexpr absl::string_view kFormatString = "Welcome to %s, Number %d!";
std::string s = absl::StrFormat(kFormatString, "The Village", 6);
Requiring the format string to be constexpr allows compile-time checking of
the format strings.
NOTE: * a format string must either be declared constexpr or dynamically
formatted using an absl::ParsedFormat type. See
Advanced Formatting below.
The str_format library mostly follows the POSIX syntax as used within the
POSIX printf() family specification, which specifies the makeup of a
format conversion specifier. (Exceptions are noted below.)
A format conversion specifier is a string of the following form:
% charactern$, where n is a
non-negative, positive value. (E.g. 3$, 10$, etc.). Note that positional
modifiers are fully supported in StrFormat; they are a POSIX extension and
not part of standard printf notation.- to left-justify the result. (Right-justification is the default.)+ to force prepending a plus sign to positive results. (Minus signs
are always prepended.)+
takes precedence over spaces).# to use an alternative form of conversion for certain specifiers.
(E.g. Using # on a hex conversion will prepend 0x or 0X to hex
string results.)0 (zero) to pad leading zeros for integer and floating-point
conversions. (Zero-padding is ignored for integers if precision is
explicitly specified.) This flag is ignored if - is used.n to specify the minimum width of
the result, or *variable to use a variable of type int
to specify this value..n, where n is a integral
value, or .*variable to use a variable of type int to
specify this value.StrFormat(),
these values are largely ignored (and not needed, since StrFormat() is
type-safe) but are allowed for backwards compatibility:
hh, h, l, ll, L, j, z, t, q
There is one case where the length modifier has a visible effect: if the
requested type is c, an l modifier causes the supplied argument to be
treated as a wchar_t instead of a char. (This happens automatically if
the supplied argument is already of type wchar_t.)c for character values. These will be treated like char unless the
supplied type is wchar_t or an l modifier is present, in which case
they will be treated like wchar_t and converted to multibyte strings
encoded as UTF-8.s for string values. Wide strings (std::wstring,
std::wstring_view) will be converted to multibyte strings encoded as
UTF-8.d or i for integer values, including enumerated type valueso for unsigned integer conversions, including enumerated type values,
into octal valuesx or X for unsigned integer conversions, including enumerated type
values, into hex valuesu for unsigned integer valuesf or F for floating point values into decimal notatione or E for floating point values into exponential notationa or A for floating point values into hex exponential notationg or G for floating point values into decimal or exponential
notation based on their precisionp for pointer address valuesn for the special case of writing out the number of characters written
to this point.v for values using the default format for a deduced type. These
deduced types include many primitive types denoted here as well as
user-defined types containing the proper extensions. (See
User Defined Formats below.)NOTE: the n specifier within the printf family of functions is unsafe.
StrFormat() allows use of %n only when capturing such values within a safe
FormatCountCapture class. See example below.
NOTE: the v specifier (for “value”) is a type specifier not present in the
POSIX specification. %v will format values according to their deduced type.
v uses d for signed integer values, u for unsigned integer values, g for
floating point values, and formats boolean values as "true"/"false" (instead
of 1 or 0 for booleans formatted using d). const char* is not supported;
please use std::string and string_view. char is also not supported due to
ambiguity of the type. This specifier does not support modifiers.
Examples:
// Characters
absl::StrFormat("%c", 'a') -> "a"
absl::StrFormat("%c", 32) -> " "
absl::StrFormat("%c", 100) -> "d"
absl::StrFormat("%lc", 0x2002) -> (Locale-dependent) // E.g. U+2002 as UTF-8
// Strings
absl::StrFormat("%s", "Hello!") -> "Hello!"
// Decimals
absl::StrFormat("%d", 1) -> "1"
absl::StrFormat("%02d", 1) -> "01" // Zero-padding
absl::StrFormat("%-2d", 1) -> "1 " // Left justification
absl::StrFormat("%0+3d", 1) -> "+01" // + specifier part of width
// Octals
absl::StrFormat("%o", 16) -> "20"
absl::StrFormat("%o", 016) -> "16" // literal octal
absl::StrFormat("%#o", 016) -> "016" // alternative form
// Hex
absl::StrFormat("%x", 16) -> "10"
absl::StrFormat("%x", 0x16) -> "16"
absl::StrFormat("%#x", 0x16) -> "0x16" // alternative form
absl::StrFormat("%X", 10) -> "A" // Upper-case
absl::StrFormat("%#06x", 0x16) -> "0x0016" // "0x" counts as part of the width
// Unsigned Integers
absl::StrFormat("%u", 16) -> "16"
absl::StrFormat("%u", -16) -> "4294967280"
// Big Integers
// Length modifiers are unnecessary, and are ignored
absl::StrFormat("%d", 100'000'000'000'000) -> "100000000000000"
absl::StrFormat("%lld", 100'000'000'000'000) -> "100000000000000"
// Floating Point
// Default precision of %f conversion is 6
absl::StrFormat("%f", 1.6) -> "1.600000"
absl::StrFormat("%05.2f", 1.6) -> "01.60" // Width includes decimal pt.
absl::StrFormat("%.1f", 1.63232) -> "1.6" // Rounding down
absl::StrFormat("%.3f", 1.63451) -> "1.635" // Rounding up
absl::StrFormat("%*.*f", 5, 2, 1.63451) -> " 1.63" // Same as "%5.2f"
// Exponential Notation
// Default precision of a %e conversion is 6
// Default precision of exponent is 2
// Default sign of exponent is +
absl::StrFormat("%e", 1.6) -> "1.600000e+00"
absl::StrFormat("%1.1e", 1.6) -> "1.6e+00"
// Hex Exponents
absl::StrFormat("%a", 3.14159) -> "0x1.921f9f01b866ep+1"
// Floating Point to Exponential Notation
absl::StrFormat("%g", 31415900000) -> "3.14159e+10"
// Pointer conversion
int* ptr = 9;
absl::StrFormat("%p", ptr) -> "0x7ffdeb6ad2a4";
// Positional Modifiers
std::string s = absl::StrFormat("%2$s, %3$s, %1$s!", "vici", "veni", "vidi");
EXPECT_EQ(s, "veni, vidi, vici!");
// Character Count Capturing
int n = 0;
std::string s = absl::StrFormat(
"%s%d%n", "hello", 123, absl::FormatCountCapture(&n));
EXPECT_EQ(8, n);
// %v
std::string s = "hello";
unsigned int x = 16;
absl::StrFormat("%v", s) -> "hello"
absl::StrFormat("%v", 1) -> "1"
absl::StrFormat("%v", x) -> "16"
absl::StrFormat("%v", 1.6) -> "1.6"
absl::StrFormat("%v", true) -> "true"
StrFormat() intrinsically supports all of these fundamental C++ types:
charsigned charunsigned charwchar_tstd::stringstd::wstringstd::string_view (if available)std::wstring_view (if available)intshortunsigned shortunsignedlongunsigned longlong longunsigned long longfloatdoublelong doubleUnlike the printf family of functions, StrFormat() doesn’t rely on callers
encoding the exact types of arguments into the format string. (With printf()
this must be carefully done with length modifiers and conversion specifiers -
such as %llu encoding the type unsigned long long.) In the str_format
library, a format conversion specifies a broader C++ conceptual category
instead of an exact type. For example, %s binds to any string-like argument,
so std::string, std::wstring, absl::string_view, const char*, and
const wchar_t* are all accepted. Likewise, %d accepts any integer-like
argument, etc.
Format strings that are very frequently used or performance-critical can be
specified using an absl::ParsedFormat. An absl::ParsedFormat represents a
pre-parsed absl::FormatSpec with template arguments specifying a collection of
conversion specifiers.
In C++14, these conversion specifiers are restricted to single char values (e.g.
d); in C++17 or later, you may also specify one or more
absl::FormatConversionCharSet enums (e.g. absl::FormatConversionCharSet::d
or absl::FormatConversionCharSet::d | absl::FormatConversionCharSet::x using
the bitwise-or combination.
Some enums specify whole conversion groups:
absl::FormatConversionCharSet::kIntegral = d | i | u | o | x | Xabsl::FormatConversionCharSet::kFloating = a | e | f | g | A | E | F | Gabsl::FormatConversionCharSet::kNumeric = kIntegral | kFloatingabsl::FormatConversionCharSet::kString = sabsl::FormatConversionCharSet::kPointer = pThese type specifiers will be checked at compile-time. This approach is much
faster than reparsing const char* formats on each use.
// Verified at compile time.
static const auto* const format_string =
new absl::ParsedFormat<'s','d'>("Welcome to %s, Number %d!");
absl::StrFormat(*format_string, "TheVillage", 6);
// Verified at runtime.
auto format_runtime = absl::ParsedFormat<'d'>::New(format_string);
if (format_runtime) {
value = absl::StrFormat(*format_runtime, i);
} else {
... error case ...
}
// C++17 allows extended formats to support multiple conversion characters per
// argument, specified via a combination of `FormatConversionCharSet` enums.
using MyFormat = absl::ParsedFormat<absl::FormatConversionCharSet::d |
absl::FormatConversionCharSet::x>;
MyFormat GetFormat(bool use_hex) {
if (use_hex) return MyFormat("foo %x bar");
return MyFormat("foo %d bar");
}
Pre-compiled formats can also be used as a way to pass formats through API boundaries in a type-safe manner. The format object encodes the type information in its template arguments to allow compile-time checking in the formatting functions.
Example:
// Note: this example only compiles in C++17 and above.
class MyValue {
public:
// MyValueFormat can be constructed from a %d or a %x format and can be
// used with any argument type that can be formatted with %d or %x.
using MyValueFormat = absl::ParsedFormat<absl::FormatConversionCharSet::d |
absl::FormatConversionCharSet::x>;
const MyValueFormat& GetFormat(int radix) const {
return radix == RADIX_HEX ? format_x_ : format_d_;
}
private:
const MyValueFormat format_d_{"%6d"};
const MyValueFormat format_x_{"%8x"};
};
std::string PrintIt(const MyValue& foo) {
return absl::StringF(foo.GetFormat(mode), my_int_value_);
}
In addition to the std::sprintf()-like StrFormat() function, str_format.h
also provides a number of drop-in replacements for std::printf(),
std::fprintf() and std::snprintf():
absl::PrintF()absl::FPrintF()absl::SNPrintF()These functions are all analogs to the C builtin functions. In particular, they
take the same arguments, return an int with the same semantics and can set
errno. Use these functions as you would use any printf variant.
Example:
absl::PrintF("Trying to request TITLE: %s USER: %s\n", title, user);
The absl::StrAppendFormat() function allows you to perform printf-like
formatting to an existing &dest string, appending the formatted string to it.
StrAppendFormat() returns *dest as a convenience for chaining purposes.
Example:
std::string& absl::StrAppendFormat(&dest, format, ...)
absl::StreamFormat() returns an object that can be efficiently streamed to a
std::ostream, such as I/O or files.
NOTE: the returned object must be used immediately. That is, do not retain it in an ‘auto’ variable.
Example:
// Stream to standard output
std::cout << absl::StreamFormat("name: %-20.4s: quota: %7.3f", name, quota);
// Stream to a file
if (FILE* file_handle = fopen("myfile.txt", "w"); file_handle != nullptr) {
int result =
absl::FPrintF(file_handle, "%s", "C:\\Windows\\System32\\");
return result;
}
The str_format library provides customization utilities for formatting
user-defined types using StrFormat(). As with most type extensions, you should
own the type you wish to extend.
Tip: For types you don’t own you can use
absl::FormatStreamed()to format types that have anoperator<<but no intrinsic type support withinStrFormat().absl::PrintF("My Foo: %s\n", absl::FormatStreamed(foo));
There are two methods of formatting user-defined types:
AbslStringify() provides a simpler user API using the v type specifier,
and as well as working with StrFormat(), also works with absl::StrCat(),
absl::Substitute(), logging and GoogleTest.1AbslFormatConvert() is more customizable, allowing users more control over
type specifiers and additional modifiers for formatting their types.We’ll cover both of these approaches below.
AbslStringify()To make a type support the AbslStringify() extension point, define a suitable
AbslStringify() function template for that type as described below. For a
class type, AbslStringify() should be defined as a friend function template.
For an enumerated type E, define AbslStringify() at namespace scope in the
same namespace as E so that it can be found by argument-dependent looking
(ADL).
An AbslStringify() overload should have the following signature:
template <typename Sink>
void AbslStringify(Sink& sink, const UserDefinedType& value);
Note: AbslStringify() utilizes a generic “sink” buffer to construct its
string. For information about supported operations on AbslStringify()’s sink,
see https://abseil.io/docs/cpp/guides/abslstringify.
AbslStringify() only supports use with the type specifier %v, which uses
type deduction for formatting purposes.
An example usage within a user-defined type is shown below:
struct Point {
...
// StrFormat support is added to the Point class through an AbslStringify()
// friend declaration.
template <typename Sink>
friend void AbslStringify(Sink& sink, const Point& p) {
absl::Format(&sink, "(%d, %d)", p.x, p.y);
}
int x;
int y;
}
Formatting a Point can then simply use the %v type specifier:
// StrFormat has built-in support for types extended with AbslStringify
absl::StrFormat("The point is %v", p);
// AbslStringify also automatically includes support for absl::StrCat and
// absl::Substitute()
absl::StrCat("The point is ", p);
absl::Substitute("The point is $0", p);
Additionally, AbslStringify() itself can use %v within its own format
strings to perform this type deduction. Our Point above could be formatted as
"(%v, %v)" for example, and deduce the int values as %d.
AbslFormatConvert()To extend formatting to your custom type using AbslFormatConvert(), provide an
AbslFormatConvert() overload as a free (non-member) function within the same
file and namespace of that type, usually as a friend definition. The
str_format library will check for such an overload when formatting
user-defined types using StrFormat().
An AbslFormatConvert() overload should have the following signature:
absl::FormatConvertResult<...> AbslFormatConvert(
const X& value,
const absl::FormatConversionSpec& conversion_spec,
absl::FormatSink *output_sink);
absl::FormatConvertResult return value holds the set of
absl::FormatConversionCharSet values valid for this custom type. A return
value of true indicates the conversion was successful; if false is
returned, StrFormat() will produce an empty string and this result will be
propagated to FormatUntyped().absl::FormatConversionSpec holds the fields pulled from the user string as
they are processed. See “Conversion Specifiers” above for full documentation
on this format.absl::FormatSink holds the formatted string as it is built.The absl::FormatConversionSpec class also has a number of member functions to
inspect the returned conversion character specification:
conversion_char() returns the basic conversion character for this format
operation.width() and precision() indicate that the conversion operation should
adjust the resulting width or precision of the result.is_basic() indicates that no additional conversion flags are included in the
conversion, including any for modifying the width or precision. This method is
useful for optimizing conversions via a fast path.has_left_flag() indicates whether the result should be left justified,
through use of a ‘-‘ character in the format string. E.g. “%-s”has_show_pos_flag() indicates whether a sign column is prepended to the
result for this conversion character in the format string, even if the result
is positive, through use of a ‘+’ character in the format string. E.g. “%+d”has_sign_col_flag() indicates whether a mandatory sign column is added to
the result for this conversion character, through use of a space character
(‘ ‘) in the format string. E.g. “% i”has_alt_flag() indicates whether an “alternate” format is applied to the
result for this conversion character. E.g. “%#h”has_zero_flag() indicates whether zeroes should be prepended to the result
for this conversion character instead of spaces, through use of the ‘0’
character in the format string. E.g. “%0f”These member functions can be used to select how to process conversion operations encountered in the source format strings.
An example usage within a user-defined type is shown below:
struct Point {
...
// StrFormat support is added to the Point class through an
// AbslFormatConvert() friend declaration.
//
// FormatConvertResult indicates that this formatting extension will accept
// kIntegral ( d | i | u | o | x | X) or kString (s) specifiers. Successful
// conversions will return `true`.
friend absl::FormatConvertResult<absl::FormatConversionCharSet::kString |
absl::FormatConversionCharSet::kIntegral>
AbslFormatConvert(const Point& p,
const absl::FormatConversionSpec& spec,
absl::FormatSink* s) {
if (spec.conversion_char() == absl::FormatConversionChar::s) {
// If the conversion char is %s, produce output of the form "x=1 y=2"
absl::Format(s, "x=%vy=%v", p.x, p.y);
} else {
// If the conversion char is integral (%i, %d ...) , produce output of the
// form "1,2". Note that no padding will occur here.
absl::Format(s, "%v,%v", p.x, p.y);
}
return {true};
}
int x;
int y;
};
bool absl::Format(&dest, format, ...)
Similar to absl::StrAppendFormat, but the output is an arbitrary destination
object that supports the RawSink interface. To implement this interface,
provide an overload of AbslFormatFlush() for your sink object:
void AbslFormatFlush(MySink* dest, absl::string_view part);
where dest is the pointer passed to absl::Format(). This is usually
accomplished by providing a free function that can be found by ADL.
The library already provides builtin support for using sinks of type
std::string, std::ostream, and absl::Cord with absl::Format().
Note: Remember that only the type owner should write extensions like this. An
overload for the type MySink should only be declared in the header that
declares MySink, and in the same namespace as MySink. If a particular type
does not support this extension ask the owner to write one, or make your own
wrapper type that supports it.
See https://abseil.io/docs/cpp/guides/abslstringify for more documentation on other libraries that
support AbslStringify() ↩