0

There are a lot of approaches how to iterate trough std::tuple. And it is similar to range-based-for loop. I want to do something like this, but with indices of tuple, to get access to elements of various tuples.

For example I have tuple of different types, but all of them has same free functions / operators std::tuple<float, std::complex, vec3, vec4> and I want to do some operation between two or more such tuples.

I tried to write something like this:

template<typename Lambda, typename... Types, int... Indices>
void TupleIndexElems_Indexed(TTuple<Types...>, Lambda&& Func, TIntegerSequence<int, Indices...>)
{
    Func.template operator()<Indices...>();
}

template<typename TupleType, typename Lambda>
void TupleIndexElems(Lambda&& Func)
{
    TupleIndexElems_Impl(TupleType{}, Func);
}


template<typename... Types, typename Lambda>
void TupleIndexElems_Impl(TTuple<Types...>, Lambda&& Func)
{
    TupleIndexElems_Indexed(TTuple<Types...>{}, Func, TMakeIntegerSequence<int, sizeof...(Types)>{});
}

Usage:

    FSkyLightSettings& operator+=(FSkyLightSettings& Other)
    {
        auto Tup1 = AsTuple();
        auto Tup2 = Other.AsTuple();

        using TupType = TTuple<float*, FLinearColor*, FLinearColor*>;

        auto AddFunc = [] <typename Tup, int Index> (Tup t1, Tup t2)
        {
            *t1.template Get<Index>() = (*t1.template Get<Index>()) + (*t2.template Get<Index>());
        };
            
        TupleIndexElems<TupType>([=]<int... Indices>
        {
            AddFunc.template operator()<TupType, Indices>(Tup1, Tup2); // How to fold it?
        });
        return *this;
    }

I thought the best way to do it is using variaic lambda template, but when I tried to call it, I confused about impossibility to use fold expression.

Are there any elegant solutions to do that (for various versions of C++)?

UPD: I've also tried to use recursive lambda, but I can't due to compiler error C3536:

    auto PlusVariadic = [=]<int Index, int... Indices>
    {
        Plus.template operator()<TupType, Index>(Tup1, Tup2); // How to fold it?
        if constexpr (Index != 0)
        {
            PlusVariadic.operator()<Indices...>();
        }
    };
Artem Selivanov
  • 1,867
  • 1
  • 27
  • 45

1 Answers1

2

One convenient way in C++20 I use to iterate tuples is to create a constexpr_for function that calls a lambda with a std::integral_constant parameter to allow indexing, as described in my Achieving 'constexpr for' with indexing post.

#include <utility>
#include <type_traits>

template<size_t Size, typename F>
constexpr void constexpr_for(F&& function) {
    auto unfold = [&]<size_t... Ints>(std::index_sequence<Ints...>) {
        (std::forward<F>(function)(std::integral_constant<size_t, Ints>{}), ...);
    };

    unfold(std::make_index_sequence<Size>());
}

example usage:

#include <tuple>
#include <iostream>

int main() {
    auto Tup1 = std::make_tuple(1, 2.0, 3ull, 4u);
    auto Tup2 = std::make_tuple(1ull, 2.0f, 3.0, (char)4);

    constexpr auto size = std::tuple_size_v<decltype(Tup1)>;

    constexpr_for<size>([&](auto i) {
        std::get<i>(Tup1) += std::get<i>(Tup2);
        std::cout << "tuple<" << i << "> = " << std::get<i>(Tup1) << '\n';
    });
}

Output:

tuple<0> = 2
tuple<1> = 4
tuple<2> = 6
tuple<3> = 8

Try it out on godbolt.

Stack Danny
  • 7,754
  • 2
  • 26
  • 55