Search code examples
c++pipelinestd-rangesfmtc++23

Range pipeline drops typedef used for formatting style


With C++23, we get pretty-printing of ranges, currently available in {fmt}. E.g. std::set is formatted with {} whereas sequence containers such as std::vector are formatted with []. Internally, the formatter class template dispatches on the presence of a nested typedef key_type (which std::set does have and std::vector does not).

Piping a std::set into any kind of view will drop the key_type typedef and pretty-print it as a std::vector.

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

int main()
{
    auto s = std::set<int>({ 2, 3, 5, 7 });

    fmt::println("{}", s);                                              // nice, formats as { 2, 3, 5, 7 }
    fmt::println("{}", s | std::views::reverse);                        // meh, now uses [] instead of {}
    fmt::println("{}", s | std::views::reverse | std::views::reverse);  // and it won't recover the braces
}

Godbolt link

Question: is there any way to preserve the "set-ness" (more generally: the formatting kind) of an input range in a pipeline?


Solution

  • Most properties of a container are not propagated to views into those containers. The only properties which are are those which are relevant to that container being a range: iterator categories, sizeability, etc. This is in part because logically a view does not have to follow the rules of the container it is a view of.

    Consider set. A set's elements are ordered and unique (relative to that order). A view of those elements don't have to maintain this property. A transform view of a set could use a hash-like function that generates the same value from multiple different inputs, violating the unique requirement of the set. It could transform elements such that they don't maintain the ordering relationship (or transform them into elements that cannot be ordered).

    In your case, to reverse a set fundamentally changes the nature of the set. After all, the ordering function is part of the set's type; it is an inherent part of that container. If you reverse the ordering, you're using a different ordering. At best, it is a set with a different type.

    A map maps from keys to value. A key_view of a map... is just a sequence of keys. The fundamental nature of the container is lost.