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.