Strf allows you not only to add printable types, but also to override existing ones. The procedure is similar. The main difference is that instead of a PrintTraits type, you create a facet of the print_override_c category, which is almost the same. So this document presumes you already know how to do that. If you don’t, click here to get some explanation.

The facet shall contain the make_printer_input member function templates that will replace those defined in the PrintTraits class of the overrided type. For example, one could define a facet to override the bool like this:

struct italian_bool_facet
{
    using category = strf::print_override_c;

    template <typename CharT, typename Preview, typename FPack>
    constexpr static auto make_printer_input
        ( Preview& preview, const FPack&, bool x ) noexcept
    {
        return strf::make_printer_input<CharT>
            ( preview
            , strf::pack()
            , strf::conv(x ? "vero" : "falso") );
    }

    template <typename CharT, typename Preview, typename FPack, typename…​ T>
    constexpr static auto make_printer_input
        ( Preview& preview
        , const FPack& fp
        , strf::value_with_formatters<T...> x ) noexcept
    {
        return strf::make_printer_input<CharT>
            ( preview
            , fp
            , strf::conv(x.value() ? "vero" : "falso")
                .set_alignment_format(x.get_alignment_format()) );
    }
};

Everything in make_printer_input ( semantics, return type, arguments, etc ) is just like as in the PrintTrait requirements, except that here it is allowed to be non-static, though it must be then const ( so instead of hardcoded strings like "vero", and "falso" , we could use member variables, which would probably make more sense ).

Just as it is usual in PrintTraits classes, you can see that we have two make_printer_input fuctions ( though the first one is unnecessary ). The second one handles bool values with formatting. Even if we don’t define it, an expression like strf::right(true, 10, '.') is still well-formed, because the format functions that are applicable to a printable type keep being the same when we override it. We can’t change them. So it makes sense to overload make_printer_input with value_with_formatters argument even if you don’t want to support formatting, just to add a static_assert to emit a clear error message.

But if do you want support formatting then check in the documentation what are the format functions ( or the Formatters ) applicable to the printable type you are overriding. If you take a look at the part the covers bool, you can see that we only need to handle alignment formatting. And that’s what we did in the implementation above.

Things are more lenient regarding facets: you can completely ignore the facet categories that influence the original printable type, as well as consider others or new ones. You can see that although bool type is influenced by lettercase facet our override just ignores it.

Now we are almost ready to use our implementation. There is just a detail you must remember before using a facet object of the print_override_c category — you must constrain it to the types it aims to override. If you don’t that, i.e. if you use the facet directly, then Strf will apply it to all overridables types:

auto str = strf::to_string.with(italian_bool_facet{})
    (true, '/', false, '/', 1, '/', 0, '/', 1.0, '/', 0.0, '/', (void*)0);
assert(str == "vero/falso/vero/falso/vero/falso/falso");

That’s certainly not what we want. What we we want is to apply italian_bool_facet to bool arguments only, and we do do the following to achieve this:

template <typename T>
struct is_bool: std::is_same<T, strf::override_tag<bool>> {};

constexpr auto italian_bool = strf::constrain<is_bool>(italian_bool_facet{});

strf::override_tag<X> is a type alias to strf::print_traits_of<X>::override_tag, which is usually the same as strf::print_traits_of<X>::forwarded_type, which is usually the same as std::remove_cvref_t<X>. In fact, strf::override_tag<bool> is an alias to bool, but it is a better practice to use strf::override_tag instead.

When strf::print_traits_of<X> ( the PrintTraits class of X ) does not have the member type alias override_tag, then X is not overridable. An example of an non-overridable type is char ( that’s why '\' was printed as "\" and not as "vero" in the previous snippet ). You can use strf::is_overridable<X> to check at compile-time whether a type X is overridable.

Ok, now thinks should work:

auto str = strf::to_string.with(italian_bool)
    (true, '/', false, '/', 1, '/', 0, '/', 1.0, '/', 0.0, '/', (void*)0);
assert(str == "vero/falso/1/0/1/0/0x0");

// and with formatting:

str = strf::to_string.with(italian_bool)
    ( strf::center(true, 10, '.'), '/'
    , strf::center(false, 10, '.') );
assert(str == "...vero.../..falso...");

You may be wondering why printable types are overriden this way, i.e. through facets. Couldn’t this library have adoped another method, like the tag_invoke pattern ? The problem of using tag_invoke for this purpose or any other customization point technique is that the customization point is activated by the inclusion of a header, and headers may be included unintentionally. You definitely don’t want to accidentally change how a value is printed.