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_view
absl::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" // Width includes decimal pt.
absl::StrFormat("%05.2f", 1.6) -> "01.60"
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:
char
signed char
unsigned char
wchar_t
std::string
std::wstring
std::string_view
(if available)std::wstring_view
(if available)int
short
unsigned short
unsigned
long
unsigned long
long long
unsigned long long
float
double
long double
Unlike 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 | X
absl::FormatConversionCharSet::kFloating
= a | e | f | g | A | E | F | G
absl::FormatConversionCharSet::kNumeric
= kIntegral | kFloating
absl::FormatConversionCharSet::kString
= s
absl::FormatConversionCharSet::kPointer
= p
These 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()
↩