我正在尝试指定一个格式化程序来将 2 级的
mdspan
打印为矩阵,非常有用。我至少希望元素格式能够工作,例如std::println("{::.2e}", matrix)
将打印一些双精度矩阵,其格式为 .2e
。困难在于这必须在编译时完成,我不明白这通常是如何完成的,"{:" + std::string{spec} + "}"
这里不是编译时所以它失败了。了解这是如何完成的或获取实现格式化的一般提示会很有用。
#include <print>
#include <mdspan>
#include <format>
template<class T, class Extents, class Layout, class Accessor>
requires (Extents::rank() == 2)
struct std::formatter<std::mdspan<T, Extents, Layout, Accessor>> {
std::string_view spec;
constexpr auto parse(format_parse_context& ctx) {
auto it = ctx.begin();
auto end = ctx.end();
spec = std::string_view{it, end};
return end;
}
auto format(const std::mdspan<T, Extents, Layout, Accessor>& md, format_context& ctx) const {
auto out = ctx.out();
*out++ = '[';
for (size_t i = 0; i < md.extent(0); ++i) {
if (i > 0) {
*out++ = '\n';
*out++ = ' ';
}
*out++ = '[';
for (size_t j = 0; j < md.extent(1); ++j) {
if (j > 0)
*out++ = ' ';
if (spec.empty())
out = std::format_to(out, "{}", md[i, j]);
else
out = std::format_to(out, "{:" + std::string{spec} + "}", md[i, j]);
}
*out++ = ']';
}
*out++ = ']';
return out;
}
};
int main() {
std::println("{::.2e}", std::mdspan((double []) {1.1111, 2, 3, 4}, 2, 2));
}
过去,对我来说,尽可能多地使用已实现的功能效果最好,尤其是在
std::formatter
方面。避免繁重工作的一种方法是进行以下更改:
std::formatter<T>
以从已实现的parse
功能中受益。std::formatter<T>::format(md[i,j], ctx);
处理数字格式并推进上下文输出迭代器。一个工作示例可能如下所示:
#include <print>
#include <mdspan>
#include <format>
template<class T, class Extents, class Layout, class Accessor>
requires (Extents::rank() == 2)
struct std::formatter<std::mdspan<T, Extents, Layout, Accessor>> : std::formatter<T>
{
auto format(const std::mdspan<T, Extents, Layout, Accessor> & md, format_context & ctx) const {
auto out = ctx.out();
*out++ = '[';
for (size_t i = 0; i < md.extent(0); ++i)
{
if (i > 0)
{
*out++ = '\n';
*out++ = ' ';
}
*out++ = '[';
for (size_t j = 0; j < md.extent(1); ++j)
{
if (j > 0)
{
*out++ = ' ';
}
std::formatter<T>::format(md[i, j], ctx);
}
*out++ = ']';
}
*out++ = ']';
return out;
}
};
int main()
{
auto arr = std::array{ 1.1111, 2.0, 3.0, 4.0 };
auto span = std::mdspan(arr.data(), 2, 2);
std::println("{:.2e}", span);
}
此外,如果
parse
,则 ctx.begin()
返回的迭代器应该返回 .begin() == .end()
- 如果不是,那么它应该指向第一个不匹配的字符(如果它是 }
),否则应该抛出 format_error
。您的实现点超出了最后一个字符,并且将/可能导致编译错误。