What's the simple way to print or format a custom type with libfmt?

Viewed 103

Suppose I've defined some type, e.g.

struct Foo { int a; float b; };

If I want to stream it to an ostream, I write an operator<< function, e.g.:

std::ostream& operator<<(std::ostream& os, const Foo& foo)
{
  return os << '(' << a << ',' << b << ')';
}

Now I want to do the same thing, but in an fmt::format() or fmt::print() call. If I write:

fmt::print("{}\n", foo);

I'll get a bunch of errors, ending with something like:

/path/to/fmt/core.h:1073:9: error: static assertion failed: Cannot format argument. 
To make type T formattable provide a formatter<T> specialization:
https://fmt.dev/latest/api.html#formatting-user-defined-types

Ok, so, I go there, and I see a bunch of examples, already the first of which is kind of complicated. What's the simplest thing I can write to achieve what I want?

1 Answers

To simplify the example, let's assume you are only willing to accept {} formatting specifiers, without any additional string controlling how printing looks like. In that case, you would write this:

template <> class fmt::formatter<Foo> {
public:
  constexpr auto parse (format_parse_context& ctx) { return ctx.begin(); }
  template <typename Context>
  constexpr auto format (Foo const& foo, Context& ctx) const {
      return format_to(ctx.out(), "({}, {})", foo.a, foo.b);  // --== KEY LINE ==--
  }
};

That's it. A bit baroque, and you can't remove the first method (why?...) but not that terrible. Copy-paste this snippet from here; replace Foo with your type; and rewrite the actual formatting command on the KEY LINE.

Full example program:

#include <fmt/format.h>

struct Foo { int a; float b; };

template <>
class fmt::formatter<Foo> {
public:
  constexpr auto parse (format_parse_context& ctx) { return ctx.begin(); }
  template <typename Context>
  constexpr auto format (Foo const& foo, Context& ctx) const {
      return format_to(ctx.out(), "({},{})", foo.a, foo.b);
  }
};

int main () {
  Foo foo {123, 4.56};
  fmt::print("{}\n", foo);
}

See it working on GodBolt.

Related