在为自定义类型实现格式化程序时如何有效地转发格式化参数

How do I efficiently forward the formatting arguments when implementing a formatter for a custom type

我正在尝试编写一个自定义格式化程序来帮助我打印矢量。我正在尝试维护格式说明符,以便矢量中的每个项目都以相同的方式格式化。

我的大部分灵感来自 this tutorial, and 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", std::vector{ 0.0, 321.123, 654398.4328, -0.0000000000012345, 2374651273.7236457862345});
    return 0;
}

输出:

{0.000, 321.123, 654398.433, -0.000, 2374651273.724}
{0.0e+00, 3.2e+02, 6.5e+05, -1.2e-12, 2.4e+09}

复制格式字符串似乎过于笨拙且效率低下,以便我可以将其反馈给另一个 fmt::format 调用,尤其是当扩展的 class : fmt::formatter<ValueType> 已经为我们提供时内部有一个完全有效的 parse 函数,(我在这个例子中重新实现了它只是为了以一种 hacky 的方式获得所需的输出)。

我真的很想删除自定义解析实现并替换行

format_to(out, formatString, item);

format_to(out, fmt::formatter<ValueType>::format(item, context))

除非valid/doesn无法编译。

正确的做法是什么?


注意:我完全意识到在我的示例中扩展类型毫无意义,并且我可以将它作为局部变量但是我正在尝试重用 class 的功能,因此扩展这感觉是正确的方向,即使我还没有找到解决方案。


我找到的所有其他示例的列表,这些示例还没有帮助我:

你可以摆脱你的parse实现并使用继承的函数,并在你的format中使用fmt::formatter<ValueType>::format(item, context)来输出每个项目(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", std::vector{ 0.0, 321.123, 654398.4328, -0.0000000000012345, 2374651273.7236457862345});
    return 0;
}

您可以在 Formatting User-defined Types 下的文档中查看此模式的其他示例。