BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bslfmt_format

Detailed Description

Provide a standard compliant format implementation.

Outline

Purpose

Provide a standard compliant format implementation.

Classes

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:

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:

An example of a user defined formatter is as follows:

namespace bsl {
template <class t_CHAR>
struct formatter<UserDefinedType, t_CHAR> {
template <class t_PARSE_CONTEXT>
t_PARSE_CONTEXT::iterator parse(t_PARSE_CONTEXT& pc)
{
// implementation goes here
}
template <class t_FORMAT_CONTEXT>
t_FORMAT_CONTEXT::iterator format(UserDefinedType s,
t_FORMAT_CONTEXT& ctx) const
{
// implementation goes here
}
};
} // close namespace bsl
#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;
bsl::string res = bsl::format("{:#06x}", value);
assert(bsl::string("0x0063") == res);
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:

/// This class implements a complex-constrained, value-semantic type for
/// representing dates. Each object of this class *always* represents a
/// *valid* date value in the range `[0001JAN01 .. 9999DEC31]` inclusive.
class Date {
private:
// DATA
int d_year; // year
int d_month; // month
int d_day; // day
public:
// CREATORS
/// Create an object having the value represented by the specified
/// `year`, `month`, and `day`.
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));
}
// ACCESSORS
/// Return the year of this date.
int year() const { return d_year; }
/// Return the month of this date.
int month() const { return d_month; }
/// Return the day of this date.
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.

/// This struct is a base class for `bsl::formatter` specializations for
/// the `Date` class.
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.

// PRIVATE TYPES
enum Format {
e_NUMERIC, // 1999-10-23
e_VERBAL // 23 October 1999
};
// DATA
Format d_format; // output format
// PRIVATE ACCESSORS
/// Output the specified `yearValue` to the specified `outIterator`.
/// The specified `paddingRequired` indicates whether additional
/// characters need to be added to fill empty space.
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);
}
/// Output the specified `monthOrDayValue` to the specified
/// `outIterator`. The specified `paddingRequired` indicates whether
/// an additional character needs to be added to fill empty space.
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:
// CREATORS
/// Create a formatter that outputs values in the `e_NUMERIC` format.
/// Thus, numeric is the default format for the `Date` object.
: d_format(e_NUMERIC)
{
}
// MANIPULATORS
/// Parse the specified `context` and return end iterator of parsed
/// range.
template <class t_PARSE_CONTEXT>
BSLS_KEYWORD_CONSTEXPR_CPP20 typename t_PARSE_CONTEXT::iterator parse(
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.

// Handling empty string or empty specification
if (current == end || *current == '}') {
return context.begin(); // RETURN
}
// Reading format specification
switch (*current) {
case 'V':
case 'v': {
d_format = e_VERBAL;
} break;
case 'N':
case 'n': {
// `e_NUMERIC` value is assigned at object construction
} break;
default: {
BSLS_THROW(bsl::format_error(
"Unexpected symbol in format specification")); // THROW
}
}
// Move the iterator to the next position and check that there are
// no extra characters in the description.
++current;
if (current != end && *current != '}') {
BSLS_THROW(bsl::format_error(
"Too many symbols in format specification")); // THROW
}
context.advance_to(current);
return context.begin();
}
// ACCESSORS
/// Create string representation of the specified `value`, customized
/// in accordance with the requested format and the specified
/// `formatContext`, and copy it to the output that the output iterator
/// of the `formatContext` points to.
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) { // 23 October 1999
static const char *const months[] = {"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"};
// Outputting day
outputMonthDay<t_FORMAT_CONTEXT>(outIterator,
value.day(),
false);
outIterator = BloombergLP::bslfmt::FormatterCharUtil<
t_CHAR>::outputFromChar(' ', outIterator);
// Outputting month
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);
// Outputting year
outputYear<t_FORMAT_CONTEXT>(outIterator, value.year(), false);
}
else if (e_NUMERIC == d_format) { // 1999-10-23
// Outputting year
outputYear<t_FORMAT_CONTEXT>(outIterator, value.year(), true);
outIterator = BloombergLP::bslfmt::FormatterCharUtil<
t_CHAR>::outputFromChar('-', outIterator);
// Outputting month
outputMonthDay<t_FORMAT_CONTEXT>(outIterator,
value.month(),
true);
outIterator = BloombergLP::bslfmt::FormatterCharUtil<
t_CHAR>::outputFromChar('-', outIterator);
// Outputting day
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.

namespace bsl {
template <class t_CHAR>
struct formatter<Date, t_CHAR> : DateFormatter<t_CHAR> {
};
} // close namespace bsl

Finally, we create a Date object, output it to the string and verify the result:

Date date(1999, 10, 23);
bsl::string result = bsl::format("{:v}", date);
assert(bsl::string("23 October 1999") == result);
result = bsl::format("{:N}", date);
assert(bsl::string("1999-10-23") == result);