假设我定义了某种类型,例如
struct Foo { int a; float b; };
如果我想将其流式传输到ostream,我会编写一个operator<<
函数,例如:
std::ostream& operator<<(std::ostream& os, const Foo& foo)
{
return os << '(' << a << ',' << b << ')';
}
现在我想做同样的事情,但在fmt::format()
或fmt::print()
调用中。如果我写:
fmt::print("{}n", foo);
我会得到一堆错误,以这样的东西结束:
/path/to/fmt/core.h:1073:9: error: static assertion failed: Cannot format argument.
To make type T formattable provide a formatter<T> specialization:
https://fmt.dev/latest/api.html#formatting-user-defined-types
好吧,我去那里,我看到了很多例子,其中第一个已经有点复杂了。我能写什么最简单的东西来实现我想要的?
为了简化示例,假设您只愿意接受{}
格式说明符,而不需要任何额外的字符串来控制打印效果。在这种情况下,您将写下:
template <> class fmt::formatter<Foo> {
public:
constexpr auto parse (format_parse_context& ctx) { return ctx.begin(); }
template <typename Context>
constexpr auto format (Foo const& foo, Context& ctx) const {
return format_to(ctx.out(), "({}, {})", foo.a, foo.b); // --== KEY LINE ==--
}
};
就是这样。有点巴洛克风格,你不能去掉第一种方法(为什么?…(,但也没那么可怕。从这里复制粘贴此片段;将Foo
替换为您的类型;并重写KEY LINE上的实际格式化命令。
完整示例程序:
#include <fmt/format.h>
struct Foo { int a; float b; };
template <>
class fmt::formatter<Foo> {
public:
constexpr auto parse (format_parse_context& ctx) { return ctx.begin(); }
template <typename Context>
constexpr auto format (Foo const& foo, Context& ctx) const {
return format_to(ctx.out(), "({},{})", foo.a, foo.b);
}
};
int main () {
Foo foo {123, 4.56};
fmt::print("{}n", foo);
}
看看它在GodBolt上的工作。
这也适用:
#include "spdlog/fmt/ostr.h"
struct Foo { int a; float b; };
std::ostream& operator<<(std::ostream& os, const Foo& foo) {
return os << "(" << foo.a << "," << foo.b << ")";
}
int main () {
Foo foo {123, 4.56};
fmt::print("{}n", foo);
}
Godbolt:https://godbolt.org/z/vrEWGP1fn