使用 {fmt} 库进行打印

8
我能使用fmt库打印C++类的对象吗?
fmt::print("The object is {}.", obj);

1
C++类的对象是什么? - Lightness Races in Orbit
1
我认为你只需要阅读文档 - Lightness Races in Orbit
很可能不行。print函数并不知道obj的布局或者你想要如何打印obj - Thomas Matthews
2个回答

15

是的,这是可能的。正如评论中建议的那样,fmt 直接提供了自定义类型的支持: 格式化用户定义的类型

我通常喜欢使用替代方法,即使用 std::ostream。当您为 std::ostream 和您的自定义类型实现 operator<< 时,只要您也包含了 <fmt/ostream.h>fmt 就能够格式化您的自定义类型。例如:

#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

为什么会“慢得多”? - Lightness Races in Orbit
4
@LightnessRaceswithMonica, 这与 iostream 相关,iostream 是出了名的缓慢。我无法发布一个指向 quick-bench 的链接,因为它没有安装 fmt,我会更新我的答案以支持这个说法。 - Ton van den Heuvel
好的,这样就更好了。 - Lightness Races in Orbit
你尝试过使用 std::ios_base::sync_with_stdio 吗?这可能会产生重大差异。 - Oliver Schönrock
1
@OliverSchönrock,这没有任何影响,因为fmt不使用标准输出流,而是使用内存流(我怀疑是std::stringstream,但我没有验证),这不受sync_with_stdio的影响。 - Ton van den Heuvel

11

是的。您可以通过提供一个针对您的类型的formatter特化来实现,如格式化用户定义类型中所述:

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

您还可以通过组合或继承来重用现有的格式化程序,这种情况下,您可能只需要实现format函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接