Печать с использованием библиотеки {fmt} - PullRequest
1 голос
/ 28 октября 2019

Можно ли напечатать объект класса C ++ с использованием библиотеки fmt ?

fmt::print("The object is {}.", obj);

Ответы [ 2 ]

2 голосов
/ 29 октября 2019

Да. Вы можете сделать это, предоставив специализацию formatter для вашего типа, как описано в Форматирование пользовательских типов :

#include <fmt/format.h>

struct point { double x, y; };

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

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

Вы также можете повторно использовать существующие средства форматирования с помощью композиции или наследования, в которыхВ этом случае вам может понадобиться только реализовать функцию format.

2 голосов
/ 28 октября 2019

Да, это возможно. Как указано в комментариях, fmt обеспечивает непосредственную поддержку пользовательских типов: Форматирование пользовательских типов .

Обычно я предпочитаю альтернативный подход с использованием std::ostream. Когда вы реализуете operator<< для std::ostream и ваш пользовательский тип fmt сможет форматировать ваш пользовательский тип при условии, что вы также включите <fmt/ostream.h>. Например:

#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;
}

Имейте в виду, что этот подход, вероятно, будет намного медленнее, чем первоначальное предложение о прямом прохождении fmt.

Обновление: Кподдерживая утверждение о том, что использование <fmt/ostream.h> медленнее, чем непосредственно через fmt, вы можете использовать следующий тест (с помощью 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;
}

Вывод на мою машину:

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
...