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, charset)

fmt(arg).sani(charset)

conv(arg, charset)

fmt(arg).conv(charset)

sani(arg)

fmt(arg).sani()

conv(arg)

fmt(arg).cont()

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.

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(unsigned 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(unsigned 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(unsigned 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(unsigned 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::size_t count)

Prints the argument count times.

String formatting

Member function Effect

p(width_t precision)

Sets string precision

template <typename Charset> sani(Charset cs)

Equivalent to sanitize_from_charset(cs)

template <typename Charset> conv(Charset cs)

Equivalent to convert_from_charset(cs)

conv()

Equivalent to convert_from_charset()

sani()

Equivalent to sanitize_from_charset()

template <typename Charset> convert_from_charset(Charset cs)

Transcodes the input string from the charset represented by cs, if it is not already destination charset.

template <typename Charset> sanitize_from_charset(Charset cs)

Transcodes the input string from the charset represented by cs, or just sanitizes it, if cs is also the destination charset.

convert_charset()

Transcodes the input string from the charset that correspods to its character type, if it is not already the same as the destination charset.

sanitize_charset()

Transcodes the input string from the charset that corresponds to its character type, or just sanitizes it, if it same as the destination charset.

Destinations

Expression Header

to (outbuff_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*

  • outbuff_ref is a basic_outbuff<CharT>&, where CharT is a character type.

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

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

basic_outbuff<CharT>&

Return value

outbuff_ref

Supports reserve

No

See the list of types that derive from basic_outbuff<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_writer<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

basic_char_array_writer<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...)

basic_outbuff classes

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

Type Description

basic_cstr_writer<CharT>

Writes C strings

discarded_outbuff<CharT>

Discard content

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<CharTopt>

Writes to FILE* using narrow-oriented functions.

wide_cfile_writer

Writes to FILE* using wide-oriented functions.

where:

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 exists, 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

invalid_seq_notifier_c

Yes

Callback to notify nonconformities to the character encoding.

surrogate_policy_c

Yes

Wheter surrogates are treated as errors

tr_error_notifier_c

No

Callback to notify errors on the tr-string

width_calculator_c

Yes

Defines how the width is calculated

print_override_c

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 correspondig 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 conv:

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

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

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

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

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

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

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

auto str = strf::to_string
    .with(strf::iso_8859_3<char>)    // the output charset
    ( strf::conv("--\xff--")                           // not sanitized
    , strf::conv("--\xff--", strf::iso_8859_3<char>)   // not sanitized ( same charset )
    , strf::conv("--\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 the 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 charset, or that is impossible to write, in a conformant way, in the destination charset. But there is an optional exception for surrogates characters.

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 the 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.

Surrogates tolerance

There is one particular kind of nonconformity that you may sometimes want to permit, which is the invalid presence of surrogate characters. That is particular common on Windows, where you may have an old file names, created at the time of Windows 95 ( where wide strings were UCS-2 ) and that contains some unpaired surrogates. If you then treat it as UTF-16 and convert it to UTF-8 and back to UTF-16, you get a different name.

So the library provides the surrogate_policy enumeration, which is a facet that enables you to turn off the surrogate sanitization.

namespace strf {
enum class surrogate_policy : bool { strict = false, lax = true };
}

When the value is surrogate_policy::strict, which is the default, if an UTF-16 input contains a high surrogate not followed by a low surrogate, or a low surrogate not following a high surrogate, that is considered invalid and is thus sanitized. When the value is surrogate_policy::lax, those situations are allowed.

std::u16string original {u'-', 0xD800 ,u'-', u'-', u'-'};

// convert to UTF-8
auto str_strict = strf::to_u8string(strf::conv(original));
auto str_lax =
    strf::to_u8string .with(strf::surrogate_policy::lax) (strf::conv(original));

assert(str_strict == u8"-\uFFFD---");                  // surrogate sanitized
assert(str_lax == (const char8_t*)"-\xED\xA0\x80---"); // surrogate allowed

// convert back to UTF-16
auto utf16_strict = strf::to_u16string(strf::conv(str_lax));
auto utf16_lax =
    strf::to_u16string .with(strf::surrogate_policy::lax) (strf::conv(str_lax));

assert(utf16_strict == u"-\uFFFD\uFFFD\uFFFD---");     // surrogate sanitized
assert(utf16_lax == original);                         // surrogate preserved

Encoding error notifier function

The facet invalid_seq_notifier contains a function pointer that is called every time an ivalid sequence is sanitized, unless it is null, which is the default.

The code below throws an exception if u16str contains any invalid sequence:

std::u16string u16str = /* ... */;
notifier_func =  [](){
    throw std::sytem_error(std::make_error_code(std::errc::illegal_byte_sequence));
};
strf::invalid_seq_notifier notifier{ notifier_func };

auto str = strf::to_string.width(notifier)(strf::conv(u16str));

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., btye 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 � ). This is the default.

    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
  • The make_width_calculator function template takes a function object f as paramenter and returns a facet object that calculates the width of the strings by converting them to UTF-32 ( following the policy associated to invalid_seq_notifier::replace ) and then calling f to evaluate the width of each UTF-32 character. f shall take a char32_t parameter and return a width_t, which is a type that implements Q16.16 fixed-point arithmetics. This means that can use non itegral values.

    Example
    auto wfunc = [](char32_t ch) → strf::width_t {
        using namespace strf::width_literal;
        static const strf::width_t roman_numerals_width [] = {
            0.5642_w, 1.1193_w, 1.6789_w, 1.8807_w, 1.2982_w, 1.8853_w,
            2.4954_w, 3.0046_w, 1.8945_w, 1.3624_w, 1.9035_w, 2.4771_w,
            1.1789_w, 1.4495_w, 1.4128_w, 1.7294_w
        };
    
        if (ch < 0x2160 || ch > 0x216F) {
            return 1;
        }
        return roman_numerals_width[ch - 0x2160];
    };
    auto my_wcalc = strf::make_width_calculator(wfunc);
    auto str = u8"\u2163 + \u2167 = \u216B"; // "Ⅳ + Ⅷ = Ⅻ"
    auto result = strf::to_u8string.with(my_wcalc) (strf::right(str, 18, '.'));
    
    // width calculated as 13.3624, rounded to 13:
    assert(result == u8".....\u2163 + \u2167 = \u216B");
  • The fifth option is to implement your own width calculator. This implies to create a class that satisfies the WidthCalculator type requirements. There are two reasons why you may want to do that, instead of the of the previous options:

    • Accuracy: The previous methods are not able to take into account the presence of ligatures and digraphs.

    • Peformance: The object returned by make_width_calculator converts the string to UTF-32 before calling the provided function object for each UTF-32 character. When you implement your own calculator, you can optimize it to directly measure strings that are encoded in a specific charset.


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 conv 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::conv(str) > 12);
auto res2 = strf::to_u16string.with(strf::width_as_fast_u32len)
            (strf::conv(str) > 12);
auto res3 = strf::to_u16string.with(strf::width_as_u32len)
            (strf::conv(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...);