string_view
operator+
vs. StrCat()
absl::Status
std::bind
absl::optional
and std::unique_ptr
absl::StrFormat()
make_unique
and private
Constructors.bool
explicit
= delete
)switch
Statements Responsibly= delete
AbslHashValue
and Youcontains()
std::optional
parametersif
and switch
statements with initializersinline
Variablesstd::unique_ptr
Must Be MovedAbslStringify()
vector.at()
auto
for Variable DeclarationsAbslStringify()
Originally posted as TotW #215 on November 2, 2022
By Phoebe Liang
Updated 2022-11-16
Quicklink: abseil.io/tips/215
Abseil now contains a new lightweight mechanism, AbslStringify()
, that allows
users to format user-defined types as strings. User-defined types that are
extended using AbslStringify()
work out of the box with absl::StrFormat
,
absl::StrCat
and absl::Substitute
.
As with most type extensions, you should own the type you wish to extend.
Let’s say that we have a simple Point
struct:
struct Point { int x; int y; };
If we want a Point
to be formattable with absl::StrFormat()
,
absl::StrCat()
and absl::Substitute()
, we add a friend
function template
named AbslStringify()
:
struct Point { template <typename Sink> friend void AbslStringify(Sink& sink, const Point& p) { absl::Format(&sink, "(%d, %d)", p.x, p.y); } int x; int y; };
Note: AbslStringify()
utilizes a generic “sink” buffer to construct its
string. This sink has an interface similar to absl::FormatSink
, but does not
support PutPaddedString()
.
Now absl::StrCat("The point is ", p)
and absl::Substitute("The point is $0",
p)
will just work.
Note: absl::StrFormat()
also provides
a more customizable extension point
AbslFormatConvert()
that is not supported by absl::StrCat()
.
%v
SpecifierBut what if we want to format our type using absl::StrFormat()
?
absl::StrFormat()
’s existing type specifiers don’t support user-defined types
extended with AbslStringify()
, so we would have to do something like this:
absl::StrFormat("The point is (%d, %d)", p.x, p.y)
This is obviously not ideal. It doesn’t make use of the extension at all and it
duplicates the format string used in the AbslStringify()
definition. Instead,
we can use the new type specifier %v
:
absl::StrFormat("The point is %v", p)
%v
uses type deduction to format an argument. %v
supports the formatting of
most primitive types, as well as any types extended using AbslStringify()
. You
can think of %v
as a generic way to format a “value” of any type that
absl::StrFormat()
can deduce. %v
can also be used directly within
AbslStringify()
definitions.
The %v
specifier deduces the following types:
d
for signed integral valuesu
for unsigned integral valuesg
for floating point values
double
float
long double
s
for string values
std::string
absl::string_view
std::string_view
absl::Cord
NOTE: const char*
is not supported. See below for more information.
Some examples:
absl::StrFormat("%v", std::string{"hello"}) -> "hello" absl::StrFormat("%v", 42) -> "42" absl::StrFormat("%v", uint64_t{16}) -> "16" absl::StrFormat("%v", 1.6) -> "1.6" absl::StrFormat("%v", true) -> "true"
%v
intentionally does not support char
and const char*
due to ambiguity in
the desired output format. Boolean values are printed as "true"
and "false"
rather than "1"
and "0"
, which is how absl::StrFormat()
and
absl::StrCat()
print booleans otherwise.
AbslStringify()
has additional support throughout other Abseil libraries.
Types that define AbslStringify()
are directly loggable:
struct Point { template <typename Sink> friend void AbslStringify(Sink& sink, const Point& p) { absl::Format(&sink, "(%v, %v)", p.x, p.y); } int x = 10; int y = 20; }; Point p; LOG(INFO) << p;
This code will produce a message in the logs like:
I0926 09:00:00.000000 12345 main.cc:10] (10, 20)
It is recommended that custom types be made loggable by implementing
AbslStringify()
rather than operator<<
as it is a universal stringification
extension that also enables absl::StrFormat
, absl::StrCat
and
absl::Substitute
support.
Protocol buffers are formattable using AbslStringify()
. Since
AbslStringify()
provides an overall smoother user experience over
DebugString()
, it is recommended that users use AbslStringify()
when
formatting protos as strings.
message MyProto { optional string my_string = 1 } MyProto my_proto; my_proto.set_my_string("hello world"); absl::StrCat("My proto is: ", my_proto); absl::StrFormat("My proto is: %v", my_proto); LOG(INFO) << my_proto;
Aside from user-defined types where its use is required, the %v
type specifier
is intended for use in cases where the precise formatting is not important. It
is essentially a catch-all “just print it in a human readable manner” specifier.
If you require any other guarantees beyond that, please use one of the more
specific type specifiers.