Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Printing using {fmt} library

Tags:

c++

fmt

Can I print an object of the C++ class using fmt library?

fmt::print("The object is {}.", obj);
like image 364
sadig Avatar asked Apr 15 '26 16:04

sadig


2 Answers

Yes, it is possible. As suggested in the comments, fmt provides support for custom types directly: Formatting user defined types.

I normally prefer an alternative approach using std::ostream. When you implement operator<< for std::ostream and your custom type, fmt will be able to format your custom type provided that you include <fmt/ostream.h> as well. For example:

#include <fmt/format.h>
#include <fmt/ostream.h>

struct A {};

std::ostream& operator<<(std::ostream& os, const A& a)
{
  return os << "A!";
}

int main()
{
  fmt::print("{}\n", A{});
  return 0;
}

Keep in mind that this approach will likely be much slower than the initial suggestion of going through fmt directly.

Update: To support the claim that using <fmt/ostream.h> is slower than going through fmt directly, you can use the following benchmark (using Google Benchmark):

#include <fmt/format.h>
#include <fmt/ostream.h>

#include <benchmark/benchmark.h>

struct A {};

std::ostream& operator<<(std::ostream& os, const A& a)
{
  return os << "A!";
}

struct B {};

template<>
struct fmt::formatter<B>
{
  template<typename ParseContext>
  constexpr auto parse(ParseContext& ctx)
  {
    return ctx.begin();
  }

  template<typename FormatContext>
  auto format(const B& b, FormatContext& ctx)
  {
    return format_to(ctx.out(), "B!");
  }
};

static void BM_fmt_ostream(benchmark::State& state)
{
  for (auto _ : state)
  {
    benchmark::DoNotOptimize(fmt::format("{}", A{}));
  }
}

static void BM_fmt_direct(benchmark::State& state)
{
  for (auto _ : state)
  {
    benchmark::DoNotOptimize(fmt::format("{}", B{}));
  }
}

BENCHMARK(BM_fmt_direct);
BENCHMARK(BM_fmt_ostream);

int main(int argc, char** argv)
{
  benchmark::Initialize(&argc, argv);
  benchmark::RunSpecifiedBenchmarks();
  return 0;
}

Output on my machine:

2019-10-29 12:15:57
Running ./fmt
Run on (4 X 3200 MHz CPU s)
CPU Caches:
  L1 Data 32K (x2)
  L1 Instruction 32K (x2)
  L2 Unified 256K (x2)
  L3 Unified 4096K (x1)
Load Average: 0.53, 0.50, 0.60
***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
------------------------------------------------------
Benchmark               Time           CPU Iterations
------------------------------------------------------
BM_fmt_direct          42 ns         42 ns   16756571
BM_fmt_ostream        213 ns        213 ns    3327194
like image 65
Ton van den Heuvel Avatar answered Apr 17 '26 06:04

Ton van den Heuvel


Yes. You can do this by providing a formatter specialization for your type as described in Formatting User-defined Types:

#include <fmt/format.h>

struct point { double x, y; };

template <> struct fmt::formatter<point> {
  constexpr auto parse(format_parse_context &ctx) { return ctx.begin(); }

  template <typename FormatContext>
  auto format(const point &p, FormatContext &ctx) const {
    return format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y);
  }
};

You can also reuse existing formatters via composition or inheritance in which case you might only need to implement the format function.

like image 37
vitaut Avatar answered Apr 17 '26 04:04

vitaut



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!