String Formatting#
Many places in Serenity allow you to format strings, similar to printf(), for example DeprecatedString::formatted()
, StringBuilder::appendff(), or dbgln(). These are checked at compile time to ensure the format string matches the
number of parameters. The syntax is largely based on
the C++ std::formatter syntax
but there are some differences.
For basic usage, any occurrences of {} in the format string are replaced with the other arguments, converted to string
form, in order:
DeprecatedString::formatted("Well, {} my {} friends!", "hello", 42) == "Well, hello my 42 friends!";
If you want to include a literal { in the output, use {{:
DeprecatedString::formatted("{{ {}", "hello") == "{ hello";
You can refer to the arguments by index, if you want to repeat one or change the order:
DeprecatedString::formatted("{2}{0}{1}", "a", "b", "c") == "cab";
To control how the arguments are formatted, add colon after the optional index, and then add format specifier characters:
DeprecatedString::formatted("{:.4}", "cool dude") == "cool";
DeprecatedString::formatted("{0:.4}", "cool dude") == "cool";
Format specifiers#
In order, the format can contain:
- Fill character and alignment
- Sign
#Hash0Zero- Width
- Precision
- Type specifier
Each of these is optional. You can include any combination of them, but they must be in this order.
Fill and alignment#
This is an optional fill character, followed by an alignment. The fill character can be anything apart from { or },
and is used to fill any space left when the input has fewer characters than the format requests. By default, it is a
space. ( )
The alignment characters are:
<: Align left.>: Align right.^: Align centered.
Sign#
+: Always display a sign before the number.-: Display a sign for negative numbers only.- (space): Display a sign for negative numbers, and a leading space for other numbers.
Hash#
# causes an "alternate form" to be used.
For integer types, this adds the number-base prefix after the sign:
0bfor binary.0for octal.0xfor hexadecimal.
Zero#
0 pads the number with leading zeros.
Width and Precision#
The width defines the minimum number of characters in the output. The precision is a . followed by a precision number,
which is used as the precision of floating-point numbers, or a maximum-width for string values.
Both the width and precision can be provided as a replacement field ({}, optionally including an argument index) which
allows you to use an integer argument instead of a hard-coded number.
Type specifiers#
| Type | Effect | Example output |
|---|---|---|
| nothing | default format | Anything! :^) |
| b | binary | 110, 0b000110 |
| B | binary uppercase | 110, 0B000110 |
| d | decimal | 42, +0000042 |
| o | octal | 043 |
| x | hexadecimal | ff0, 0x00000ff0 |
| X | hexadecimal uppercase | FF0, 0X00000FF0 |
| c | character | a |
| s | string | well, hello friends! |
| p | pointer | 0xdeadc0de |
| f | float | 1.234, -inf |
| a | hex float | |
| A | hex float uppercase | |
| hex-dump | hexadecimal dump | fdfdfdfd, 3030 00 |
Not all type specifiers are compatible with all input types, of course.
Formatting custom types#
You can provide a custom AK::Formatter<Foo> class to format Foo values. For the simplest case where you already have
a function that produces a string from your type, that would look like this:
template<>
struct AK::Formatter<Web::CSS::Selector> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, Web::CSS::Selector const& selector)
{
return Formatter<StringView>::format(builder, selector.serialize());
}
};
More advanced formatters that make check for format-specifier flags can be written by referring to the fields
in StandardFormatter (which most Formatter classes extend).
Detecting if a type can be formatted#
The AK::HasFormatter<T> template has a boolean value representing whether T can be formatted.
The FormatIfSupported<T> makes use of this to return either the formatted value of T, or a series of ?s if the
type cannot be formatted. For example:
// B has a Formatter defined, but A does not.
DeprecatedString::formatted("{}", FormatIfSupported { A {} }) == "?";
DeprecatedString::formatted("{}", FormatIfSupported { B {} }) == "B";