假设我定义了某种类型,例如
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
替换为您的类型;并在关键行上重写实际的格式化命令。
完整示例程序:
#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);
}
我用一个简单的解决方案让它工作
struct Custom {
std::string format() { return "my-custom-data"; }
};
template <> struct fmt::formatter<Custom> : formatter<std::string> {
auto format(Custom const &c, format_context& ctx) {
return formatter<std::string>::format(c.format(), ctx);
}
};