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...>();
}
};
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