【问题标题】:Printing using {fmt} library使用 {fmt} 库打印
【发布时间】:2019-10-28 18:43:00
【问题描述】:

我可以使用fmt 库打印 C++ 类的对象吗?

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

【问题讨论】:

  • 什么 C++ 类的对象?
  • 我想你只需要阅读the documentation
  • 很可能,不。print 函数不知道 obj 的布局或希望如何打印 obj

标签: c++ fmt


【解决方案1】:

是的。您可以通过为您的类型提供 formatter 特化来做到这一点,如 Formatting User-defined Types 中所述:

#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】:

    是的,这是可能的。正如 cmets 中所建议的,fmt 直接提供对自定义类型的支持:Formatting user defined types

    我通常更喜欢使用std::ostream 的替代方法。当您为std::ostream 和您的自定义类型实现operator&lt;&lt; 时,如果您也包含&lt;fmt/ostream.h&gt;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 慢得多。

    更新:为了支持使用 &lt;fmt/ostream.h&gt; 比直接通过 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
    

    【讨论】:

    • 为什么会“慢很多”?
    • @LightnessRaceswithMonica,因为它涉及到众所周知的慢的 iostream。我无法发布指向 quick-bench 的链接,因为它没有安装 fmt,我将更新我的答案以支持该声明。
    • 好,这样更好
    • 你试过了吗:std::ios_base::sync_with_stdio en.cppreference.com/w/cpp/io/ios_base/sync_with_stdio 可能会有很大的不同。
    • @OliverSchönrock,这没有什么区别,因为fmt 不使用标准输出流,而是在内存流中(我怀疑std::stringstream,我还没有验证它)不受@987654337 的影响@.
    猜你喜欢
    • 2022-01-01
    • 1970-01-01
    • 2013-05-04
    • 1970-01-01
    • 1970-01-01
    • 2021-06-06
    • 2019-09-27
    • 2023-01-17
    • 2021-05-04
    相关资源
    最近更新 更多