4

While I can use <fmt/ranges.h> to readily output the contents of a std::vector<T>, I'm at a loss to format the display of its elements according to my preferences.

#include <fmt/core.h>
#include <fmt/ranges.h>

int main() {
    double x1 = 1.324353;
    double x2 = 4.432345;
    std::vector<double> v = {x1, x2};
    fmt::print("{}\n", v); // OK [1.324353, 4.432345]
    fmt::print("{:+5.2}\n", x1); // OK +1.3
    // fmt::print("{:+5.2}\n", v); // Does not compile!
    return EXIT_SUCCESS;
}

The program outputs:

[1.324353, 4.432345]
 +1.3

but is missing the desired output from the commented out line in my code above

[ +1.3, +4.4]

I then tried implementing a custom formatter for vectors of ordinary type but my attempt comes up short:

// custom formatter for displaying vectors
template <typename T>
struct fmt::formatter<std::vector<T>> : fmt::formatter<T> {
    constexpr auto parse(format_parse_context &ctx) {
        return ctx.begin();
    }

    template <typename FormatContext>
    auto format(std::vector<T> v, FormatContext &ctx) {
        std::vector<std::string> v_str;
        v_str.reserve(v.size());
        const auto fmt_str = [&]() {
            if constexpr (std::is_integral<T>::value) {
                return "{:+5}";
            } else if constexpr (std::is_floating_point<T>::value) {
                return "{:+5.2}";
            } else {
                return "{}";
            }
        }();
        for (auto &e : v) {
            v_str.push_back(fmt::format(fmt_str, e));
        }
        return format_to(ctx.out(), "{}", v);
    }
};

The compiler complains

type_traits:3174:38: error: ambiguous partial specializations of 'formatter<std::vector<double>>'
    : public integral_constant<bool, __is_constructible(_Tp, _Args...)>

How do I get the fmt library to display the contents of a vector with custom formatting? The version I'm currently using is 8.1.1.

marital_weeping
  • 618
  • 5
  • 18

1 Answers1

3

You can do it as follows:

std::vector<double> v = {1.324353, 4.432345};
fmt::print("{::+5.2}\n", v);

This prints:

[ +1.3,  +4.4]

godbolt

Note the extra :. Format specifiers after the first colon (empty in this case) apply to the vector itself. Specifiers after the second colon (+5.2) apply to elements.

vitaut
  • 49,672
  • 25
  • 199
  • 336
  • 1
    Thanks @vitaut . It would also be great if you could direct us to where the use of double `:`s is documented. I'd also like to point out that this feature works for `fmt` v.9.0.0 but not for older versions (v.8.1.1 in particular) . – marital_weeping Aug 07 '22 at 04:28
  • I have also not seen this anywhere. And saves me so much trouble! – Chris Dec 04 '22 at 10:49
  • 1
    It even works with nested vectors and one can control the brackets, see here: https://github.com/fmtlib/fmt/blob/b0c8263cb26ea178d3a5df1b984e1a61ef578950/test/ranges-test.cc#L29-L78 – Chris Dec 04 '22 at 10:57