2

Most samples of c++ books leverage recursively mechanism to print std::tuple. Is it possible to print std::tuples iteratively by leverage sizeof...(Typename)?

For example, the function signature is like below:

template<typename... Ts> constexpr void PrintTuple(std::tuple<Ts...>& tuple)

Then I could use sizeof...(Ts) to know how many elements in the tuple and then I could use std::get< i >(tuple) to retrieve the individual element?

Bostonian
  • 615
  • 7
  • 16

2 Answers2

3

Here's one of the possible solutions:

#include <cstddef>
#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>

template <typename T, std::size_t ...I, typename F>
void tuple_foreach_impl(T &&tuple, std::index_sequence<I...>, F &&func)
{
    // In C++17 we would use a fold expression here, but in C++14 we have to resort to this.
    using dummy_array = int[];
    dummy_array{(void(func(std::get<I>(tuple))), 0)..., 0};
}

template <typename T, typename F> void tuple_foreach(T &&tuple, F &&func)
{
    constexpr int size = std::tuple_size<std::remove_reference_t<T>>::value;
    tuple_foreach_impl(std::forward<T>(tuple), std::make_index_sequence<size>{},
                       std::forward<F>(func));
}

int main()
{
    auto x = std::make_tuple("Meow", 1, 2.3);

    tuple_foreach(x, [](auto &&value)
    {
        std::cout << value << ' ';
    });
    // Prints:
    // Meow
    // 1
    // 2.3
}

With tuple_foreach making a proper printer should be simple.

template <typename T> void print_tuple(const T &tuple)
{
    std::cout << '{';
    tuple_foreach(tuple, [first = true](auto &value) mutable
    {
        if (!first)
            std::cout << "; ";
        else
            first = 0;
        std::cout << value;
    });
    std::cout << '}';
}

// ...

print_tuple(std::make_tuple("Meow", 1, 2.3)); // Prints `{Meow; 1; 2.3}`
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
  • How is this different from any of the solutions on [Pretty-print std::tuple](https://stackoverflow.com/q/6245735/27678)? – AndyG Apr 16 '18 at 20:10
  • 1
    @AndyG The comment wasn't there when I started writing answer, and I didn't bother checking again before clicking 'Post Answer'. If you decide to dupe-hammer the question, I don't mind removing the answer (if I even can delete an accepted answer). – HolyBlackCat Apr 16 '18 at 20:18
  • Why do you pass function by reference (*F &&func*) and not by value as in std-library? – Fedor Jun 19 '21 at 20:23
  • 1
    @Fedor Functors can have internal state (e.g. lambdas have their captures), which I wouldn't want to copy. It doesn't really matter in this case though. – HolyBlackCat Jun 19 '21 at 20:27
0

c++20 makes everything very easy:

void print_tuple(auto&& t) noexcept
{
  [&]<auto ...I>(std::index_sequence<I...>) noexcept
  {
    (
      [&]() noexcept
      {
        if constexpr(I)
        {
          std::cout << ", ";
        }

        std::cout << std::get<I>(t);
      }(),
      ...
    );

    std::cout << '\n';
  }
  (
    std::make_index_sequence<
      std::tuple_size_v<std::remove_cvref_t<decltype(t)>>
    >()
  );
}
user1095108
  • 14,119
  • 9
  • 58
  • 116
  • This goes simpler already in C++17 as `std::apply([](auto const& ... x) {(std::cout<<...< – davidhigh Jul 31 '22 at 10:39
  • Yeah, tell this to the folks over [here](https://www.cppstories.com/2022/tuple-iteration-apply/), lol. Anyway, I kind of like the `if constexpr` and the lambda within the fold expression, seems more flexible to me. – user1095108 Jul 31 '22 at 10:51
  • 1
    It is more flexible, and C++20 indeed makes many things very easy. – davidhigh Jul 31 '22 at 10:56