Format Functions

The expression fmt(arg) returns a printable object that contains a copy or reference of arg as well as format information that can be edited with the member functions like sci and fill:

Example
auto str = strf::to_string( +*strf::fmt(1.0).sci().fill(U'~') ^ 15 );
assert(str == "~~~~+1.e+01~~~~")

These member functions are called in this library as format functions.

There are also some global function templates that work as alias to format functions:

Expression Equivalent Expression

left(arg, width)

fmt(arg) < width

right(arg, width)

fmt(arg) > width

center(arg, width)

fmt(arg) ^ width

left(arg, width, ch)

fmt(arg).fill(ch) < width

right(arg, width, ch)

fmt(arg).fill(ch) > width

center(arg, width, ch)

fmt(arg).fill(ch) ^ width

pad0(arg, width)

fmt(arg).pad0(width)

punct(arg)

fmt(arg).punct()

bin(arg)

fmt(arg).bin()

dec(arg)

fmt(arg).oct()

oct(arg)

fmt(arg).dec()

hex(arg)

fmt(arg).hex()

sci(arg)

fmt(arg).sic()

fixed(arg)

fmt(arg).fixed()

gen(arg)

fmt(arg).gen()

sci(arg, precision)

fmt(arg).sic().p(precision)

fixed(arg, precision)

fmt(arg).fixed().p(precision)

gen(arg, precision)

fmt(arg).gen().p(precision)

multi(arg, count)

fmt(arg).multi(count)

sani(arg)

fmt(arg).sani()

sani(arg, charset)

fmt(arg).sani(charset)

transcode(arg)

fmt(arg).transcode()

transcode(arg, charset)

fmt(arg).transcode(charset)

unsafe_transcode(arg)

fmt(arg).unsafe_transcode()

unsafe_transcode(arg, charset)

fmt(arg).unsafe_transcode(charset)

Example
auto str = strf::to_string( +*strf::center(1.0, 9, U'~') );
assert(str == "~~~+1.~~~")

Alignment formatting

Format functions Effect

operator<(width_t width)

Aligns to the left ( Or to the right on right-to-left (RTL) scripts, like Arabic )

operator>(width_t width)

Aligns to the right ( Or to the left on RTL scripts )

operator^(width_t width)

Center alignment

fill(char32_t ch)

Sets the fill character.

set_alignment_format(alignment_format)

Set all alignment formatting options simultaneously.

set_alignment_format(default_alignment_format)

Set all alignment formatting options to default.

clear_alignment_format()

Set all alignment formatting options to default.

Note

You can see there is no equivalent to std::internal in the above table. Instead, look for the pad0 format function if you want to pad zeros when printing integers or floating-points.

Floating-point formatting

Member function Effect

operator*()

Equivalent to the '#' format flag in printf

operator+()

Equivalent to the '+' format flag in printf

fill_sign()

Similar to the ' ' format flag in printf: prints an extra fill character ( from the alignment formatting ) before non-negative numbers.

operator~()

Equivalent to fill_sign()

punct()

Applies the numeric punctuation according to the numpunct_c facet

operator!()

Equivalent to punct()

pad0(int w)

Similar to the '0' format flag in printf:

For NaN and infinity, causes the the width ( from alignment formatting ) to be at least equal to w.

For valid numbers, prints zeros after the sign and the base indication and before the digits such that at least w characters are printed ( not counting the fill characters caused by alignment formatting, but still counting the extra fill character caused by fill_sign() ).

p(int precision)

Sets the precision. Effect varies according to the notation ( see below ).

set_float_notation(float_notation)

Sets the float notation ( see below ).

hex()

Equivalent to set_float_notation(float_notation::hex).

fixed()

Equivalent to set_float_notation(float_notation::fixed).

sci()

Equivalent to set_float_notation(float_notation::scientific).

gen()

Equivalent to set_float_notation(float_notation::general).

set_float_format(float_format)

Set all floating-point formatting options simultaneously.

set_float_format(default_float_format)

Reset all floating-point formatting options to default.

float_notation::hex

Hexadecimal

float_notation::fixed

If precision is not set, prints the smallest number of digits such that the floating-point value can be exactly recovered. If precision is set, it is the number of fractional digits.

float_notation::scientific

If precision is not set, prints the smallest number of digits such that the floating-point value can be exactly recovered. If precision is set, it is the number of fractional digits.

float_notation::general

If precision is not set, chooses the notation ( scientific or fixed ) that leads to the smallest number or characters such that the floating-point value can be exactly recovered.
If precision is set, same effect as the 'g' format flags in printf ( except that the lettercase is specified by the lettercase facet ):

  • The precision is the number of significant digts

  • If precision is 0, it is treated as 1

  • Trailing fractional zeros are removed unless operator* is used.

  • Selects the scientific notation iff the resulting exponent is less than -4 or greater than or equal to the precision

pad0 is independent of alignment formatting:
auto s = strf::to_string( strf::center(-1.25, 12, '_').pad0(8) );
assert(s == "__-0001.25__");

auto nan = std::numeric_limits<double>::quiet_NaN();

s = strf::to_string( strf::center(-nan, 12, '_').pad0(8) );
assert(s == "____-nan____");

s = strf::to_string( strf::center(-nan, 8, '_').pad0(12) );
assert(s == "____-nan____");

Integer formatting

Member function Effect

bin()

Uses the binary base.

oct()

Uses the octal base.

dec()

Uses the decimal base.

hex()

Uses the hexadecimal base.

operator*()

Equivalent to the '#' format flag in printf. Only applicable in non-decimal bases.

operator+()

Equivalent to the '+' format flag in printf. Only applicable in decimal base.

operator~()

Equivalent to fill_sign()

fill_sign()

Similar to the ' ' format flag in printf: prints an extra fill character ( specified by some of the alignment format function ) before non-negative numbers. Only applicable in decimal base.

punct()

Applies the numeric punctuation according to the numpunct_c facet

operator!()

Equivalent to punct()

pad0(int w)

Inserts zeros after the sign or base indication and before the digits such that at least w characters are printed ( not counting the fill characters caused by alignment formatting, but still counting the extra fill character caused by fill_sign() ).

p(int precision)

Inserts zeros after the sign or base indication and before the digits such that at least precision digits are printed

set_int_format(int_format)

Set all integers formatting options simultaneously.

set_int_format(default_int_format)

Reset all integers formatting options to default.

Char formatting

Member function Effect

multi(std::ptrdiff_t count)

Prints the argument count times.

String formatting

Member function Effect

p(width_t precision)

Sets string precision

template <typename Charset> transcode(Charset cs)

Transcodes the input string if cs is different from the output charset, otherwise just copies it as in memcpy. ( The output charset is defined by the facet value of the charset_c<CharOut> facet category, where CharOut is the destination character type )

template <typename Charset> unsafe_transcode(Charset cs)

Same as in trancode(cs), except that the transcoding has undefined behaviour if the input string is not fully conformant to the encoding represent by cs. It is expected to have better performace than trancode.

template <typename Charset> sani(Charset cs)

If cs is different from the ouput charset, transcodes the input string just like in transcode. If they are the same, sanitizes the input string. That is, in both cases, any non-conformity to the input charset is replaced by the replacement character ( "\uFFFD" or '?' ), and causes the function transcoding_error_notifier::invalid_sequence to be called, if the TranscodingErrorNotifierPtr facet object is not null ( see section "Transcoding error handling")

transcode()

Equivalent to transcode(cs) where cs is the facet value for the charset_c<CharIn> facet category, where CharIn is the character type of the input string.

unsafe_transcode()

Equivalent to unsafe_transcode(cs) where cs is the facet value for the charset_c<CharIn> facet category, where CharIn is the character type of the input string.

sani()

Equivalent to sani(cs) where cs is the facet value for the charset_c<CharIn> facet category, where CharIn is the character type of the input string.

Printing target expressions

Expression Header

to (destination_ref)

<strf.hpp>

to (char_ptr, end)

<strf.hpp>

to (char_ptr, count)

<strf.hpp>

to (char_array)

<strf.hpp>

to_range (char_ptr, end)

<strf.hpp>

to_range (char_ptr, count)

<strf.hpp>

to_range (char_array)

<strf.hpp>

to_basic_string <CharT, Traitsopt, Aopt>

<strf/to_string.hpp>

to_string

<strf/to_string.hpp>

to_u8string

<strf/to_string.hpp>

to_u16string

<strf/to_string.hpp>

to_u16string

<strf/to_string.hpp>

to_wstring

<strf/to_string.hpp>

to (streambuf_ptr)

<strf/to_streambuf.hpp>

to (streambuf_ref)

<strf/to_streambuf.hpp>

to <CharTopt> (cfile)

<strf/to_cfile.hpp>

wto (cfile)

<strf/to_cfile.hpp>

where:

  • CharT is a charater type.

  • Traits is a CharTraits type.

  • A is an Allocator type

  • char_ptr is a CharT* value, where CharT is a character type.

  • end is a CharT* value, where CharT is a character type.

  • count is a std::size_t value

  • streambuf_ptr is a std::streambuf<CharT, Traits>*

  • streambuf_ref is a std::streambuf<CharT, Traits>&

  • cfile is a FILE*

  • destination_ref is a destination<CharT>&, where CharT is a character type.

  • args... is an argument list of printable values.

strf::to(destination_ref) (args...)
Return type

void

Supports reserve

No

See the list of types that derive from destination<CharT>&.

strf::to(char_ptr, end)   (args...);
strf::to(char_array)      (args...);
strf::to(char_ptr, count) (args...);
Header file

<strf.hpp>

Preconditions
  • count > 0

  • end > char_ptr

Return type

basic_cstr_destination<CharT>::result

Return value

a value r, such that:

  • r.ptr points to last written character which is always '\0'.

  • r.truncated is true when the destination string is too small. In this case, the number of characters written is unspecified, but it is always greater than zero ( because '\0' is always written )

Note

The termination character '\0' is always written.

Supports reserve

No

strf::to_range(char_ptr, end)   (args...);
strf::to_range(char_array)      (args...);
strf::to_range(char_ptr, count) (args...);
Header file

<strf.hpp>

Preconditions
  • end >= char_ptr

Return type

array_destination<CharT>::result

Return value

a value r, such that:

  • r.ptr is the one-past-the-end pointer of the characters written.

  • r.truncated is true when the destination is too small. In this case, the number of characters written is unspecified.

Note

The termination character '\0' is not appended to the content.

Supports reserve

No

strf::to_basic_string <CharT, Traitsopt, Aopt> ( args... )
Return type

std::basic_string<CharT, Traits, A>

Supports reserve

Yes

strf::to_string ( args... )
Return type

std::string

Supports reserve

Yes

strf::to_u8string ( args... )
Return type

std::u8string

Supports reserve

Yes

strf::to_u16string ( args... )
Return type

std::u16string

Supports reserve

Yes

strf::to_u32string ( args... )
Return type

std::u32string

Supports reserve

Yes

strf::to_wstring ( args... )
Return type

std::wstring

Supports reserve

Yes

to(streambuf_ptr)  (args...);
to(streambuf_ref)  (args...);
Return type

basic_streambuf_writer<CharT, Traits>::result

Return value

A value r, such that:

  • r.count is equal to the number of successfully written characters.

  • r.success is false if an error occurred

Supports reserve

No

to<CharTopt>(cfile) (args...)
Effect

Successively call std::fwrite(buffer, sizeof(CharT),/*...*/, cfile) until the whole content is written or until an error happens, where buffer is an internal array of CharT.

Return type

narrow_cfile_writer<CharT>::result

Return value
  • count is sum of the returned values returned by the several calls to std::fwrite.

  • success is false if an error occured.

Supports reserve

No

wto(cfile) (args...)

destination classes

The table below lists the concrete types that derivate from the destination<CharT> abstract class.

Type Description

basic_cstr_destination<CharT>

Writes C strings. Always writes the termination character '\0'.

array_destination<CharT>

Writes to also char*, but does not append the termination character '\0'

discarder<CharT>

Discards the content. The analogous of /dev/null.

basic_string_appender<CharT, Traitsopt, Aopt>

Appends to std::basic_string objects.

basic_string_maker<CharT, Traitsopt, Aopt>

Creates std::basic_string objects.

basic_sized_string_maker<CharT, Traitsopt, Aopt>

Creates std::basic_string objects with pre-reserved capacity

basic_streambuf_writer<CharT, Traitsopt>

Writes to std::basic_streambuf object

narrow_cfile_writer<CharT, BufferSize>

Writes to FILE* using narrow-oriented functions.

wide_cfile_writer

Writes to FILE* using wide-oriented functions.

where:

  • CharT is a charater type.

  • Traits is a CharTraits type.

  • A is an Allocator type

  • BufferSize is a std::size_t constexpr value;

Tr-string

auto s = strf::to_string.tr("{} in hexadecimal is {}", x, strf::hex(x));

The tr-string is like what in other formatting libraries would be called as the format string, except that it does not specify any formatting. Its purpose is to enable your program to provide multilingual support by using translation tools like gettext.

Since it is common for the person who writes the string to be translated not being the same who translates it, the tr-string syntax allows the insertion of comments.

Table 1. Syntax
A '{' followed by until means

'-'

the next '}' or end of string

a comment

another '{'

the second '{'

an escaped '{'

a digit

the next '}' or end of string

a positional argument reference

any other character

the next '}' or end of string

a non positional argument reference

Comments
auto str = strf::to_string.tr
    ( "You can learn more about python{-the programming language, not the animal species} at {}"
    , "www.python.org" );
assert(str == "You can learn more about python at www.python.org");
Escapes

Note there is no way and no need to escape the '}' character, since it has special meaning only when corresponding to a previous ’{'

auto str = strf::to_string.tr("} {{x} {{{} {{{}}", "aaa", "bbb");
assert(str == "} {x} {aaa {bbb}");
Positional arguments

Position zero refers to the first input argument. The characters the after the digits are ignored. So they can also be used as comments.

auto str = strf::to_string.tr("{1 a person name} likes {0 a food name}.", "sandwich", "Paul");
assert(str == "Paul likes sandwich.");
Non positional arguments

The characters the after the '{' are ignored as well

auto str = strf::to_string.tr("{a person} likes {a food type}.", "Paul", "sandwich");
assert(str == "Paul likes sandwich.");

Tr-string error handling

When the argument associated with a "{" does not exist, the library does two things:

Facet Categories

Category Constrainable Description

numpunct_c<10>

Yes

Numeric punctuation for decimal base

numpunct_c<16>

Yes

Numeric punctuation for hexadecimal base

numpunct_c<8>

Yes

Numeric punctuation for octal base

numpunct_c<2>

Yes

Numeric punctuation for binary base

lettercase_c

Yes

Letter case for printing numeric and booleans values

charset_c<CharT>

No

The character encoding correponding to character type CharT

transcoding_error_notifier_c

Yes

Notifies nonconformities to the character encoding.

tr_error_notifier_c

No

Notifies errors on the tr-string

width_calculator_c

Yes

Defines how the width is calculated

printable_overrider_c<_R_>

Yes

Overrides printable types

Numeric punctuation

To apply numeric punctuation in integers and floating-point arguments you need to invoke the punct or operator! format function. You also need to pass a numpunct facet object to specify the "thousands" separator, the decimal point and the grouping pattern. The integer sequence passed to the constructor of numpunct defines the grouping. The last value is repeated, unless it is equal to -1.

auto s1 = strf::to_string.with(strf::numpunct<10>(1, 2, 3))(strf::punct(1000000000000ll));
assert(s1 == "1,000,000,000,00,0");

auto s2 = strf::to_string.with(strf::numpunct<10>(1, 2, 3, -1))(!strf::dec(1000000000000ll));
assert(s2 == "1000000,000,00,0");

The constructor of numpunct has some preconditions:

  • No more than six arguments can be passed.

  • No argument can be greater than 30.

  • No argument can be less than 1, unless it is the last argument and it’s equal to -1.

When default constructed, the numpunct has no grouping, i.e. the thousands separator is never printed.

The default thousands separator and decimal point are U',' and U'.', repectively. To change them, use the thousands_sep and decimal_point member functions:

auto my_punct = numpunct<10>{3} .thousands_sep(U'\'') .decimal_point(U':');
auto str = strf::to_string.with(my_punct) (strf::punct(1000000.5));
assert(str == "1'000'000:5");

//or as lvalue:
auto my_punct2 = numpunct<10>(3);
my_punct2.thousands_sep(U';');
my_punct2.decimal_point(U'^');

auto str = strf::to_string.with(my_punct2) (strf::punct(1000000.5));
assert(str == "1;000;000^5");

Numeric punctuation from locale

The header file <strf/locale.hpp> declares the locale_numpunct function, which returns a numpunct<10> object that reflects the current locale:

#include <strf/locale.hpp>
#include <strf/to_string.hpp>

void sample() {
    if (setlocale(LC_NUMERIC, "de_DE")) {
        const auto punct_de = strf::locale_numpunct();
        auto str = strf::to_string.with(punct_de) (*!strf::fixed(10000.5))
        assert(str == "10.000,5");
    }
}

Letter case

The lettercase facet affects the letter case when printing numeric values. The default value is strf::lowercase.

namespace strf {

enum class lettercase { lower = /*...*/, mixed = /*...*/, upper = /*...*/ };

constexpr lettercase lowercase = lettercase::lower;
constexpr lettercase mixedcase = lettercase::mixed;
constexpr lettercase uppercase = lettercase::upper;

}
Table 2. Printed numeric values examples
Value Result examples

strf::lowercase

0xab 1e+50 inf nan true false

strf::mixedcase

0xAB 1e+50 Inf NaN True False

strf::uppercase

0XAB 1E+50 INF NAN TRUE FALSE

Usage example
auto str_upper = strf::to_string.with(strf::uppercase)
    ( *strf::hex(0xabc), ' '
    , 1.0e+50, ' '
    , std::numeric_limits<FloatT>::infinity() );

assert(str_upper == "0XAB 1E+50 INF");

auto str_mixed = strf::to_string.with(strf::mixedcase)
    ( *strf::hex(0xabc), ' '
    , 1.e+50, ' '
    , std::numeric_limits<FloatT>::infinity() );

assert(str_mixed == "0xAB 1e+50 Inf");

Character encodings

The following variables templates can be used as facet objects that specify what is the character encoding associated to the character type passed as the template parameter.

namespace strf {

template <typename CharT> constexpr ascii_t<CharT>       ascii {};

template <typename CharT> constexpr iso_8859_1_t<CharT>  iso_8859_1 {};
template <typename CharT> constexpr iso_8859_2_t<CharT>  iso_8859_2 {};
// ... up to iso_8859_16

template <typename CharT> constexpr windows_1250_t<CharT>  windows_1250 {};
template <typename CharT> constexpr windows_1251_t<CharT>  windows_1251 {};
// ... up to windows_1258

template <typename CharT> constexpr utf8_t<CharT>   utf8 {};
template <typename CharT> constexpr utf16_t<CharT>  utf16 {};
template <typename CharT> constexpr utf32_t<CharT>  utf32 {};

template <typename CharT> constexpr utf_t<CharT> utf {}

} // namespace strf
Example: write in Windows-1252
auto s = strf::to_string
    .with(strf::windows_1252<char>)
    .with(strf::numpunct<10>{4, 3, 2}.thousands_sep(0x2022))
    ("one hundred billions = ", 100000000000ll);

// The character U+2022 is encoded as '\225' in Windows-1252
assert(s == "one hundred billions = 1\2250000\225000\2250000");

Charset conversion

Since the library knows the encoding corresponding to each character type, and knows how to convert from one to another, it is possible to mix input string of difference characters types, though you need to use the function transcode:

auto str   = strf::to_string( "aaa-"
                            , strf::transcode(u"bbb-")
                            , strf::transcode(U"ccc-")
                            , strf::transcode(L"ddd") );

auto str16 = strf::to_u16string( strf::transcode("aaa-")
                               , u"bbb-"
                               , strf::transcode(U"ccc-")
                               , strf::transcode(L"ddd") );

assert(str   ==  "aaa-bbb-ccc-ddd");
assert(str16 == u"aaa-bbb-ccc-ddd");

The transcode function can also specify an alternative encoding for a specific input string argument:

auto str_utf8 = strf::to_u8string
    ( strf::transcode("--\xA4--", strf::iso_8859_1<char>)
    , strf::transcode("--\xA4--", strf::iso_8859_15<char>));

assert(str_utf8 == u8"--\u00A4----\u20AC--");

The sani function has the same effect as transcode, except when the input encoding is same as the output. In this case sani causes the input to be sanitized, whereas transcode does not:

auto str = strf::to_string
    .with(strf::iso_8859_3<char>)    // the output charset
    ( strf::transcode("--\xff--")                          // not sanitized
    , strf::transcode("--\xff--", strf::iso_8859_3<char>)  // not sanitized ( same charset )
    , strf::transcode("--\xff--", strf::utf8<char>)        // sanitized ( different charset )
    , strf::sani("--\xff--")                               // sanitized
    , strf::sani("--\xff--", strf::iso_8859_3<char>) )     // sanitized

assert(str == "--\xff----\xff----?----?----?--");

The library replaces invalid sequences by the replacement character �, if the destination charset supports it ( and by '?' otherwise ).

An "invalid sequence" is any input that is non-conformant to the source character encoding, or that cannot be to encoded into the destination encoding.

When the input is UTF-8, the library follows the practice recommended by the Unicode Standard regarding to calculate how many replacement characters to print for each non-conformant input sequence. ( see for "Best Practices for Using U+FFFD" in Chapter 3 ).
The library does not sanitizes non-conformities when converting a single character, like punctuation characters or the the fill character ( they are in UTF-32 ). In this case the replacement character is only used when the destination charset is not able to print the codepoint. For example, if you use (char32_t)0xFFFFFFF as the decimal point, then it will printed as "\uFFFD" if the destination is UTF-8 or UTF-16, but if the destination is UTF-32, then the library just writes (char32_t)0xFFFFFFF verbatim.

Transcoding error handling

Sometimes just printing a replacement character to signalize an encoding conversion errors may not be enough. In this case, you can create a class deriving from transcoding_error_notifier to do something more, like throwing an exception or logging the failure.

transcoding_error_notifier::invalid_sequence virtual function is to be called when the source has any invalid sequence, while transcoding_error_notifier::unsupported_codepoint is for when the destination encoding is not able to represent a codepoint. You can see an example here

But pay attention: this type is not a facet. The actual facet must be a TranscodingErrorNotifierPtr type, which is supposed to hold a pointer ( raw or smart ) to your transcoding_error_notifier object.

#include <strf/to_cfile.hpp>
#include <strf/to_string.hpp>

class my_notifier: public strf::transcodint_error_notifier {
    //...
    // (click here to see an implementation example)
    //...
};

void sample() {
    strf::narrow_cfile_writer<char> err_dest{stderr};
    my_notifier notifer{err_dest};
    strf::transcoding_error_notifier_ptr notifier_ptr{&notifer};

    auto str = strf::to_string.with(notifier_ptr) ( /* ... */ );
    // ...
}

Width Calculation

The width_calculator_c facet value enables you to choose how the width of a string is calculated when using alignment formatting. You have five options:

  • The fast_width facet object assumes that the width of a string is equal to its size. This is the least accurate method, but it’s the fastest.

    Example
    auto str = "15.00 \xE2\x82\xAC \x80"; // "15.00 € \x80"
    auto result = strf::to_string.with(strf::fast_width)
                 ( strf::right(str, 12, '*') );
    assert(result == "*15.00 \xE2\x82\xAC \x80"); // width calculated as 11
  • The width_as_fast_u32len facet value evaluates the width of a string as the number of Unicode code points. However, differently from width_as_u32len, to gain performance, it assumes that the measured string is conformant to its charset. Nonconformities do not cause undefined behaviour, but lead to incorrect values. For example, the width of an UTF-8 string may simply be calculated as the number of bytes that are not in the range [0x80, 0xBF], i.e., byte that are not continuation bytes. This way, any extra continuation byte — that would replaced by a "\uFFFD" during sanitization — is not counted.

    Example
    auto str = "15.00 \xE2\x82\xAC \x80"; // "15.00 € \x80"
    auto result = strf::to_string .with(strf::width_as_fast_u32len)
                 ( strf::right(str, 12, '*'));
    assert(result == "****15.00 \xE2\x82\xAC \x80"); // width calculated as 8
  • The width_as_u32len facet value also evaluates the width of a string as the number of Unicode code points. But each nonconformity to the charset is counted as an extra code points ( as if it were replaced by replacement character � ).

    Example
    auto str = "15.00 \xE2\x82\xAC \x80"; // "15.00 € \x80"
    auto result = strf::to_string .with(strf::width_as_u32len)
                 ( strf::right(str, 12, '*'));
    
    assert(result == "***15.00 \xE2\x82\xAC \x80"); // width calculated as 9
  • std_width_calc is the default. It calculates the width just as specified to std::format, which means it is aware of grapheme clustering and also that the width of some codepoints is equal to 2. It doesn’t require however the string to be encoded in UTF ( internally, it is converted to UTF-32 ).

  • The fifth option is to implement your own width calculator. This implies to create a class that satisfies the WidthCalculator type requirements.


The width calculation algorithm is applied on the input, not the output string. Keep that in mind when converting from one charset to another using transcode or sani. For example, when converting from UTF-8 to UTF-16 and using the fast_width facet, the width of the string is its size in UTF-8, not in UTF-16.

auto str = "15.00 \xE2\x82\xAC \x80"; // "15.00 € \x80"

auto res1 = strf::to_u16string.with(strf::fast_width)
            (strf::transcode(str) > 12);
auto res2 = strf::to_u16string.with(strf::width_as_fast_u32len)
            (strf::transcode(str) > 12);
auto res3 = strf::to_u16string.with(strf::width_as_u32len)
            (strf::transcode(str) > 12);

assert(res1 == u" 15.00 \u20AC \uFFFD");  // width calculated as strlen(str)
assert(res2 == u"    15.00 \u20AC \uFFFD"); // width calculated as 8
assert(res3 == u"   15.00 \u20AC \uFFFD"); // width calculated as 9

Ranges

Without formatting

range(range_obj, funcopt)

range(array, funcopt)

range(begin, end, funcopt)

separated_range(range_obj, separator, funcopt)

separated_range(array, separator, funcopt)

separated_range(begin, end, separator, funcopt)

where

  • range_obj is an object whose type is a Container type

  • begin and end are iterators

  • separator is a raw string of CharT, where CharT is the destination character type.

  • func is unary a function object such that the type of expression func(x) is printable where x is an element of the range.

Examples
int arr[3] = { 11, 22, 33 };

auto str = strf::to_string(strf::range(arr));
assert(str == "112233");

str = strf::to_string(strf::separated_range(arr, ", "));
assert(str == "11, 22, 33");

auto op = [](auto x){ return strf::join('(', +strf::fmt(x * 10), ')'); };

str = strf::to_string(strf::separated_range(arr, ", ", op));
assert(str == "(+110), (+220), (+330)");

With formatting

fmt_range(range_obj)

fmt_range(array)

fmt_range(begin, end)

fmt_separated_range(range_obj, separator)

fmt_separated_range(array, separator)

fmt_separated_range(begin, end, separator)

Any format function applicable to the element type of the range can also be applied to the expression strf::fmt_range(/*...*/) or strf::fmt_separated_range(/*...*/). It causes the formatting to be applied to each element.

Example 1
std::vector<int> vec = { 11, 22, 33 };
auto str1 = strf::to_string("[", +strf::fmt_separated_range(vec, " ;") > 6, "]");
assert(str1 == "[   +11 ;   +22 ;   +33]");
Example 2
int array[] = { 11, 22, 33 };
auto str2 = strf::to_string
    ( "["
    , *strf::fmt_separated_range(array, " / ").fill('.').hex() > 6,
    " ]");

assert(str2 == "[..0xfa / ..0xfb / ..0xfc]");

Joins

Simple joins

join(args...)

Joins enables you to group a set of input arguments as one:

auto str = strf::to_string.tr("Blah blah blah {}.", strf::join("abc", '/', 123))
assert(str == "Blah blah blah abc/123")

They can be handy to create aliases:

struct date{ int day, month, year; };

auto as_yymmdd = [](date d) {
    return strf::join( strf::dec(d.year % 100).p(2), '/'
                     , strf::dec(d.month).p(2), '/'
                     , strf::dec(d.day).p(2) );
};
date d {1, 1, 1999};
auto str = strf::to_string("The day was ", as_yymmdd(d), '.');
assert(str == "The day was is 99/01/01.");

Aligned joins

You can apply any of the alignment format functions on the expression join(args...)

auto str = strf::to_string(strf::join("abc", "def", 123) > 15);
assert(str == "      abcdef123);

Or use any of the expressions below:

join_left(width, chopt) (args...)

join_right(width, chopt) (args...)

join_center(width, chopt) (args...)

where:

  • args... are the values to be printed

  • width is a value of type width_t

  • alignment is a value of type text_alignment

  • ch is a value of type char32_t

auto str = strf::to_string(strf::join_center(15, U'.')("abc", "def", 123));
assert(...abcdef123...);