Search code examples
c++fmt

Custom type {fmt} formatters with external templates, any drawbacks?


I have a header that defines all {fmt} formatters for my custom types.

To improve compile times, I wanted to reduce the dependencies of this custom formatter header, and decided to define all formatters as external templates, where the implementation is put in a .cpp, with the declarations in the header file as follows:

template<>
struct formatter<MyType> : formatter<std::string>
{
  auto format(const MyType& t, format_context& ctx);
};

extern template struct formatter<MyType>;

...and the definitions in the .cpp file:

auto formatter<MyType>::format(const MyType& t, format_context& ctx)
{
  return format_to(ctx.out, "MyType: {}", ...);
}

The main advantage is that the header file becomes much less heavy, all custom types can be forward declared and I no longer include the world just to have custom formatting of a single type within some translation unit.

However, most of the examples on implementing custom formatters with {fmt} define the format() function as a function templated on the format_context type:

template<typename FormatContext>
auto format(const MyType& t, FormatContext& ctx);

This does not really work with external templates since I would need to declare format() for all possible types of FormatContext up front. This is error prone. For now, only using fmt::format_context works and the compiler will tell me when it no longer suffices.

I would like to know what I lose by not having a format function templated on the FormatContext type. In what situations is fmt::format_context not sufficient? Is there a better way to define these custom type formatters without having to put the complete implementation in header files? I was thinking about going the std::ostream route, and then simply including <fmt/ostream.h> whenever I would want to format my types with {fmt}, but it partially defeats the purpose of using {fmt} in the first place.


Solution

  • what I lose by not having a format function templatized on the FormatContext type?

    You'll lose the ability to format through an output iterator. Previously this would mean that you won't be able to use format_to[_n]. However in the current master this limitation has been removed and both format_to and format_to_n work with format_context. Now only format string compilation may require custom output iterators.