问题描述
我正在尝试编写一个自定义格式化程序来帮助我打印矢量。我试图维护格式说明符,以便向量中的每个项目都以相同的方式格式化。
我的大部分灵感来自this tutorial,以及the docs
#include <fmt/core.h>
#include <fmt/format.h>
template<typename ValueType>
struct fmt::formatter<std::vector<ValueType>> : fmt::formatter<ValueType>
{
std::string formatString;
// Hack,copy the original format text into a std::string
constexpr auto parse(format_parse_context& ctx)
{
formatString= "{:";
for (auto iter = std::begin(ctx); iter != std::end(ctx); ++iter) {
char c = *iter;{
formatString += c;
}
if (c == '}') {
return iter;
}
}
return std::end(ctx);
}
template <typename FormatContext>
auto format(const std::vector<ValueType>& container,FormatContext& context)
{
auto&& out = context.out();
format_to(out,"{{");
typename std::vector<ValueType>::size_type count = 0;
const typename std::vector<ValueType>::size_type size = container.size();
for (const auto& item : container) {
// Use the copied format string,but really want to delegate the formatting to superclass...
format_to(out,formatString,item);
if (++count < size) {
format_to(out,",");
}
}
return format_to(out,"}}");
}
};
int main()
{
fmt::print("{:.3f}\n",std::vector{ 0.0,321.123,654398.4328,-0.0000000000012345,2374651273.7236457862345});
fmt::print("{:.1e}\n",2374651273.7236457862345});
return 0;
}
输出:
{0.000,654398.433,-0.000,2374651273.724}
{0.0e+00,3.2e+02,6.5e+05,-1.2e-12,2.4e+09}
复制格式字符串似乎过于笨拙和低效,这样我就可以将其反馈给另一个 fmt::format
调用,尤其是当扩展类 : fmt::formatter<ValueType>
已经为我们提供了一个完全有效的 {{ 1}} 内部函数,(我在这个例子中重新实现了它,只是为了以一种非常棒的方式获得所需的输出)。
parse
与
format_to(out,item);
除非它无效/无法编译。
这样做的正确方法是什么?
注意:我完全意识到在我的例子中扩展类型是毫无意义的,我可以将它作为局部变量但是我试图重用类的功能,所以扩展它感觉是正确的方向,即使我还没有找到解决方案。
我发现的所有其他例子的列表,但对我没有帮助:
- String formatting the cool way with C++20 std::format()(仅适用于直接返回对超级实现的单个调用,但不需要重新实现解析或创建新的格式字符串)
- Custom format specifier with {fmt} for custom class(丢弃格式或需要手动重新实现格式)
- The tutorial I mostly copied(几乎是我想要的,但仍然创建一个新的格式字符串,而不是重用用户最初指定的格式)
-
The Docs (同样,要么建议重新实现解析函数并使用新的格式字符串调用
format_to(out,fmt::formatter<ValueType>::format(item,context))
,尽管调用者已经指定了一个并且该类型有一个现有的解析函数。 ..)
解决方法
您可以去掉您的 parse
实现并使用继承的函数,并在您的 fmt::formatter<ValueType>::format(item,context)
中使用 format
来输出每个项目 (godbolt demo):
#include <fmt/core.h>
#include <fmt/format.h>
template<typename ValueType>
struct fmt::formatter<std::vector<ValueType>> : fmt::formatter<ValueType>
{
template <typename FormatContext>
auto format(const std::vector<ValueType>& container,FormatContext& context)
{
auto&& out = context.out();
format_to(out,"{{");
bool first = true;
for (const auto& item : container) {
if (first) {
first = false;
} else {
format_to(out,",");
}
fmt::formatter<ValueType>::format(item,context);
}
return format_to(out,"}}");
}
};
int main()
{
fmt::print("{:.3f}\n",std::vector{ 0.0,321.123,654398.4328,-0.0000000000012345,2374651273.7236457862345});
fmt::print("{:.1e}\n",2374651273.7236457862345});
return 0;
}
您可以在 Formatting User-defined Types 下的文档中查看此模式的其他示例。