Search code examples
c++tuplesfmt

Printing a comma-separated tuple using fmt library


Assume a tuple should be output in a comma-separated form:

#include <iostream>
#include <tuple>

template <typename TupleT, std::size_t... Is>
void printTupleImp(const TupleT& tp, std::index_sequence<Is...>) {
    size_t index = 0;

    std::cout << "(";
    ( ((index++ > 0 ? (std::cout << ", ", true) : true), 
       void(std::cout << std::get<Is>(tp))), ... );
    std::cout << ")";
}

template <typename TupleT, std::size_t TupSize = std::tuple_size_v<TupleT>>
void printTuple(const TupleT& tp) {
    printTupleImp(tp, std::make_index_sequence<TupSize>{});
}

int main() {
    std::tuple tp { 10, 20, "hello"};
    printTuple(tp);
}

Output: (10, 20, hello)

It looks like optimization of fold-expression went quite efficiently Compiler Explorer.

Is there a way to do the same using solely fmt instead of doing it by outputting to an ostream first, like here?

If that involves fmt::formatter specialization, how would one generate a format string?


Solution

  • Recent versions of fmt (specifically, fmt 6.0.0 and upwards) support formatting of ranges and tuples. See Range and Tuple Formatting in the documentation.

    #include <fmt/ranges.h>
    
    int main() {
        std::tuple tp { 10, 20, "hello"};
        // Prints "(10, 20, "hello")"
        fmt::print("{}", tp);
    }
    

    If you want to heavily customize the formatting of a tuple or if you're working with a very old version of fmt, you can also emulate it as follows:

    template <typename TupleT, std::size_t... Is>
    void printTupleImp(const TupleT& tp, std::index_sequence<Is...>) {
        int index = 0;
    
        fmt::print("(");
        ( (fmt::print(index++ > 0 ? ", {}" : "{}", std::get<Is>(tp))), ... );
        fmt::print(")");
    }
    

    See live example at Compiler Explorer.

    If you want a custom formatter that does this instead, you can look into recent versions of fmt and copy one from there.