是的,这是可能的。正如评论中建议的那样,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
fmt
不使用标准输出流,而是使用内存流(我怀疑是std::stringstream
,但我没有验证),这不受sync_with_stdio
的影响。 - Ton van den Heuvel是的。您可以通过提供一个针对您的类型的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
函数。
print
函数并不知道obj
的布局或者你想要如何打印obj
。 - Thomas Matthews