Provide a standard compliant format implementation.
Outline
Purpose
Provide a standard compliant format implementation.
Classes
- bsl::basic_format_arg: access to standard-compliant argument
- bsl::basic_format_args: access to formatting arguments
- bsl::basic_format_context: access to formatting state
- bsl::basic_format_parse_context: access to format string parsing state
- bsl::basic_format_string: checked format string
- bsl::formatter: template type for BDE formatters
- bsl::format_args: basic_format_args for
char
- bsl::format_error: standard-compliant exception type
- bsl::format_parse_context: basic_format_parse_context for
char
- bsl::format_string: basic_format_string for
char
- bsl::format_to_n_result: result type for format_to_n
- bsl::wformat_args: basic_format_args for
wchar_t
- bsl::wformat_parse_context: basic_format_parse_context for
wchar_t
- bsl::wformat_string: format_string for
wchar_t
Canonical Header
bsl_format.h
- See also
- ISO C++ Standard, <format>
Description
This component will provide, in the bsl namespace, wrappers around the functions and types exposed by the standard <format> header, where they are available, otherwise aliases to the bslfmt implementation.
This will provide, where a conforming library implementation is available, wrappers around the std::format, std::format_to, std::format_to_n, std::vformat, and std::vformat_to functions of which the format and vformat wrappers are allocator-aware. Where a conforming implementation or when compiling C++17 and earlier, BDE implementation is provided.
Where a BDE implementation is provided, functionality is limited to that provided by C++20 and excludes the following features:
- Support for locales other than the default ("C") locale
- Support for wide strings
- Alternative date/time representations
- Date/time directives not supported by the standard
strftime function
- Character escaping
- Compile-time format string checking
This header is not intended to be included directly. Please include <bsl_format.h> to be able to use bsl::format functionality.
User-provided Formatters
User-provided formatters are supported by the BSL implementation, just as they are by the standard library implementation. However, in order for them to be compatible with both implementations, there are specific requirements, notably:
- If you will define a formatter for your type
T, do so in the same component header that defines T itself. This avoids issues due to users forgetting to include the header for the formatter.
- Define
bsl::formatter<T>
- DO NOT define
std::formatter<T>
- Use template arguments for the format context and parse context parameters. This is essential as the parameter type passed in will depend upon underlying implementation.
- The
parse function must be constexpr in C++20, but this is not required (and may not be possible) for earlier C++ standards.
An example of a user defined formatter is as follows:
template <class t_CHAR>
struct formatter<UserDefinedType, t_CHAR> {
template <class t_PARSE_CONTEXT>
t_PARSE_CONTEXT::iterator parse(t_PARSE_CONTEXT& pc)
{
}
template <class t_FORMAT_CONTEXT>
t_FORMAT_CONTEXT::iterator format(UserDefinedType s,
t_FORMAT_CONTEXT& ctx) const
{
}
};
}
#define BSLS_KEYWORD_CONSTEXPR_CPP20
Definition bsls_keyword.h:645
Definition bdlat_valuetypefunctions.h:939
Usage
This section illustrates the intended use of this component.
Example 1: Simple Integer Formatting
Formatters for fundamental types are already defined, so to output such objects the bsl::format function can be used in exactly the same way as the original one from the stl library:
int value = 99;
Definition bslstl_string.h:1252
Example 2: Creating a Custom Formatter For User Defined Type
Suppose we have a custom type representing a date and we want to output it to the stream in different formats depending on the circumstances using bsl::format function. The following example demonstrates how such custom formatter may be implemented.
First, we define our Date class:
class Date {
private:
int d_year;
int d_month;
int d_day;
public:
Date(int year, int month, int day)
: d_year(year)
, d_month(month)
, d_day(day)
{
assert((1 <= year) && (9999 >= year));
assert((1 <= month) && (12 >= month));
assert((1 <= day) && (31 >= day));
}
int year() const { return d_year; }
int month() const { return d_month; }
int day() const { return d_day; }
};
Then, we define our custom formatter for this date class. In it, two methods are necessary: parse() and format(). The parse method parses the format string itself to determine the formatting to be used by the format method, which writes the formatted date into a string. Both methods are required to conform to a specific interface.
template <class t_CHAR>
struct DateFormatter {
private:
The convenience of using the bsl::format function is that the users can come up with the description language themselves. In our case, for simplicity, we will display the date in two formats - numeric (1999-10-23) and verbal (23 October 1999). Accordingly, to indicate the desired type, we will use one of two letters in the format description: 'n' ('N') or 'v' ('V'). And one field is enough for us to store it.
enum Format {
e_NUMERIC,
e_VERBAL
};
Format d_format;
template <class t_FORMAT_CONTEXT>
void outputYear(
typename t_FORMAT_CONTEXT::iterator& outIterator,
int yearValue,
bool paddingRequired) const
{
typedef BloombergLP::bslalg::NumericFormatterUtil NFUtil;
char buffer[4];
char *bufferEnd = NFUtil::toChars(buffer, buffer + 4, yearValue);
if (paddingRequired) {
const char *paddingStr = "000";
size_t numPaddingCharacters = 0;
if (1000 > yearValue) {
++numPaddingCharacters;
if (100 > yearValue) {
++numPaddingCharacters;
if (10 > yearValue) {
++numPaddingCharacters;
}
}
}
if (numPaddingCharacters) {
outIterator = BloombergLP::bslfmt::FormatterCharUtil<
t_CHAR>::outputFromChar(paddingStr,
paddingStr +
numPaddingCharacters,
outIterator);
}
}
outIterator = BloombergLP::bslfmt::FormatterCharUtil<
t_CHAR>::outputFromChar(buffer, bufferEnd, outIterator);
}
template <class t_FORMAT_CONTEXT>
void outputMonthDay(
typename t_FORMAT_CONTEXT::iterator& outIterator,
int monthOrDayValue,
bool paddingRequired) const
{
typedef BloombergLP::bslalg::NumericFormatterUtil NFUtil;
char buffer[2];
char *bufferEnd = NFUtil::toChars(buffer,
buffer + 2,
monthOrDayValue);
if (paddingRequired) {
if (10 > monthOrDayValue) {
outIterator = BloombergLP::bslfmt::FormatterCharUtil<
t_CHAR>::outputFromChar('0', outIterator);
}
}
outIterator = BloombergLP::bslfmt::FormatterCharUtil<
t_CHAR>::outputFromChar(buffer, bufferEnd, outIterator);
}
Notice that if the standard implementation of the format is supported by your compiler, then the parse function as well as the constructor must be declared as constexpr.
public:
: d_format(e_NUMERIC)
{
}
template <class t_PARSE_CONTEXT>
t_PARSE_CONTEXT& context)
{
typedef typename bsl::iterator_traits<
typename t_PARSE_CONTEXT::const_iterator>::value_type
IteratorValueType;
typename t_PARSE_CONTEXT::const_iterator current = context.begin();
typename t_PARSE_CONTEXT::const_iterator end = context.end();
#define BSLMF_ASSERT(expr)
Definition bslmf_assert.h:231
Definition bslmf_issame.h:146
bsl::format calls parse() function first and here we can configure our formatter so that it then outputs values in the format we need. context.begin() returns an iterator pointing to the end of the parsed range.
if (current == end || *current == '}') {
return context.begin();
}
switch (*current) {
case 'V':
case 'v': {
d_format = e_VERBAL;
} break;
case 'N':
case 'n': {
} break;
default: {
"Unexpected symbol in format specification"));
}
}
++current;
if (current != end && *current != '}') {
"Too many symbols in format specification"));
}
context.advance_to(current);
return context.begin();
}
template <class t_FORMAT_CONTEXT>
typename t_FORMAT_CONTEXT::iterator
format(
Date value,
t_FORMAT_CONTEXT& formatContext) const
{
typename t_FORMAT_CONTEXT::iterator outIterator =
formatContext.out();
#define BSLS_THROW(X)
Definition bsls_exceptionutil.h:374
bsl::string format(BSLFMT_FORMAT_STRING_PARAMETER fmtStr, const t_ARGS &... args)
Definition bslfmt_format_imp.h:1085
Next, we outputting the date in accordance with the previously set settings:
if (e_VERBAL == d_format) {
static const char *const months[] = {"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"};
outputMonthDay<t_FORMAT_CONTEXT>(outIterator,
value.day(),
false);
outIterator = BloombergLP::bslfmt::FormatterCharUtil<
t_CHAR>::outputFromChar(' ', outIterator);
const char *month = months[value.month() - 1];
outIterator = BloombergLP::bslfmt::FormatterCharUtil<
t_CHAR>::outputFromChar(month,
month + std::strlen(month),
outIterator);
outIterator = BloombergLP::bslfmt::FormatterCharUtil<
t_CHAR>::outputFromChar(' ', outIterator);
outputYear<t_FORMAT_CONTEXT>(outIterator, value.year(), false);
}
else if (e_NUMERIC == d_format) {
outputYear<t_FORMAT_CONTEXT>(outIterator, value.year(), true);
outIterator = BloombergLP::bslfmt::FormatterCharUtil<
t_CHAR>::outputFromChar('-', outIterator);
outputMonthDay<t_FORMAT_CONTEXT>(outIterator,
value.month(),
true);
outIterator = BloombergLP::bslfmt::FormatterCharUtil<
t_CHAR>::outputFromChar('-', outIterator);
outputMonthDay<t_FORMAT_CONTEXT>(outIterator,
value.day(),
true);
}
return outIterator;
}
};
Now, we define the bsl::formatter specialization for our Date class simply as a child-class of DateFormatter. Alternatively, we could have placed the implementation directly into the bsl::formatter specialization. Notice that the specialization must be defined in the bsl namespace.
template <class t_CHAR>
struct formatter<Date, t_CHAR> : DateFormatter<t_CHAR> {
};
}
Finally, we create a Date object, output it to the string and verify the result:
Date date(1999, 10, 23);
result = bsl::format("{:N}", date);