I want a function that will behave like std::transform
for tuples. Basically the functionality to be implemented is
template<size_t From, size_t To, class Tuple, class Func>
void tuple_transform(Tuple&& source, Tuple&& target, Func f)
{
// elements <From, To> of `target` ti become `f(si)`, where
// si is the corresponding element of `source`
};
I believe that to implement this I'll need a compile time integer range struct, a generalization of std::index_sequence
and I've implemented it here with cti::range
. I also believe that this type of compile time traversal is ideal here :
template<class Func, class Tuple, size_t...Is>
void for_each_in_tuple(Func f, Tuple&& tuple, std::index_sequence<Is...>){
using expander = int[];
(void)expander { 0, ((void)f(std::get<Is>(std::forward<Tuple>(tuple))), 0)... };
}
template<class Func, class Tuple>
void for_each_in_tuple(Func f, Tuple&& tuple){
for_each_in_tuple(f, std::forward<Tuple>(tuple),
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}
Can anyone help me with the implementation ?
@MohitJain Like in the code linked (tuple_transform) this is not taken into account (Func
has a single type). If this is solved like that, I could easily extend it by passing a template template parameter template<class> class Func
and mandate that my transformation types are like this
template<typename T>
struct Func
{
static void apply(T& val) { ... }
}
then inside the body of tuple transform, each transformation func could be called like :
get<I>(target) = func<typename tuple_element<I, Tuple>::type>::apply(get<I>(source))
Just gave a lightning talk @ accu 2015. The above was the CodeKata at the end of the presentation. I'll leave here the presentation, hopefullly it'll help with any implementation attempts (I think pretty much every tool required is presented, so we'll have more attempts at this)
Here's a solution that uses index_range
from here.
template<size_t SN, size_t DN, class TSrc, class TDest, class Func>
void tuple_call_assign(TSrc&& source, TDest& target, Func f)
{
std::get<DN>(target) = f(std::get<SN>(std::forward<TSrc>(source)));
}
template<size_t From, size_t To, class TSrc, class TDest, class Func, size_t...Is, size_t...DIs>
void tuple_transform(TSrc&& source, TDest& target, Func f,
std::index_sequence<Is...>, std::index_sequence<DIs...>)
{
using expander = int[];
(void)expander { 0, (tuple_call_assign<Is,DIs>(std::forward<TSrc>(source),target,f), 0)... };
}
template<size_t From, size_t To, size_t FromDest, class TSrc, class TDest, class Func>
void tuple_transform(TSrc&& source, TDest& target, Func f)
{
static_assert(To > From, "Range must be increasing");
static_assert(To <= std::tuple_size<std::decay_t<TSrc>>::value+1,
"Range must be valid for source tuple");
constexpr size_t RangeSize = To-From;
static_assert(FromDest+RangeSize <= std::tuple_size<std::decay_t<TDest>>::value,
"Range must be valid for target tuple");
tuple_transform<From,To>(std::forward<TSrc>(source), target, f,
index_range<From,To>(), index_range<FromDest, FromDest+RangeSize>());
}
Demo
This takes a third template argument to specify a starting index to transform into the target tuple.
The index_sequence
solution has been mentioned already:
template <std::size_t From, size_t... indices, typename T1, typename T2, typename Func>
void transform(T1&& s, T2& t, Func f, std::index_sequence<indices...>)
{
(void)std::initializer_list<int>{
(std::get<indices+From>(t) = f(std::get<indices>(std::forward<T1>(s))), 0)...};
}
template <std::size_t From, std::size_t To, typename T1, typename T2, typename Func>
void transform(T1&& s, T2& t, Func f)
{
transform<From>(std::forward<T1>(s), t, f, std::make_index_sequence<To-From+1>());
}
Demo.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With