Implementing map () and each () over std :: tuple <...> - with the index passed to the functor as a template parameter
After several years of web development, I am working in C ++ again (14) and decided to create a "dynamically typed functional pleasure" with template metaprogramming.
I have implemented map
and each
via tuples:
template <typename Tuple, typename Func, size_t... index> void tuple_each_internal(Tuple const & tuple, Func func, index_sequence<index...>) { auto res = { (func(get<index>(tuple)), nullptr)... }; } template <typename Tuple, typename Func, typename Indices = make_index_sequence<tuple_size<Tuple>::value>> void tuple_each(Tuple const & tuple, Func func) { tuple_each_internal(tuple, func, Indices()); } struct demo_functor_each { /* Case #1: "Each" callback */ template <typename T> void operator ()(T&& t) { ; } /* Case #2: "Each with index as run-time parameter" callback */ //template <typename T> //void operator ()(const size_t index, T&& t) { ; } /* Case #3: "Each with index as compile-time parameter" callback */ //template <typename T, size_t index> //void operator ()(T&& t) { ; } }; void Example_Usage() { tuple<int, bool, string> t; tuple_each(t, demo_functor_each()); }
And a similar implementation map
.
-
Case # 1 checks the syntax (I haven't tried running it yet).
-
№2 case also checks the syntax checking, with
tuple_each_internal
modified to pass the index as a parameter to the function:func(index, get<index>(tuple))
. -
Case # 3 is superior to Case # 2, in which the value
index
can be passed to other templates at compile time (egget<index>(tuple)
), which is not possible in Case # 2.
I was unable to implement case # 3.
Given the signature of the callback:
template <typename T, size_t index> void operator ()(T&& t) { ; }
I tried this like tuple_each_internal
:
auto res = { (func<typename tuple_element<index, Tuple>::type, index>(get<index>(tuple)), nullptr)... };
Clang ++ 3.6.1 output:
$ clang++ -std=c++14 tuple_iteration.h error: expected '(' for function-style cast or type construction auto res = { (func<typename tuple_element<index, Tuple>::type, index>(get<index>(tuple)), nullptr)... }; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
g ++ 5.1.0 output:
$ g++ -std=c++14 tuple_iteration.h error: expected ‘(’ before ‘,’ token auto res = { (func<typename tuple_element<index, Tuple>::type, index>(get<index>(tuple)), nullptr)... }; ^
source share
It should be:
auto res = { (func.template operator()<const typename tuple_element<index, Tuple>::type&, index>(get<index>(tuple)), nullptr)... };
Simplification with
/* Case #4: "Each with index as compile-time parameter" callback */ template <size_t index, typename T> void operator ()(T&& t);
where the code becomes:
auto res = { (func.template operator()<index>(get<index>(tuple)), nullptr)... };
or even
template <size_t N, typename T> void operator ()(std::integral_constant<std::size_t, N>, T&& t);
that leads to
auto res = { (func(std::integral_constant<std::size_t, index>{}, get<index>(tuple)), nullptr)... };
source share
For what it's worth an example of doing something like this in C ++ 17 using folding expressions for the comma operator:
//Example of creating a pack of vectors from a pack of template args, one vector for each type ("rotating" the pack 90 deg) #include <iostream> #include <vector> #include <string> #include <tuple> #include <algorithm> #include <functional> #include <numeric> #include <iterator> #include <utility>//index_sequence // //g++ -std=c++1z rotate_pack.cpp -o rotate_pack.exe // // template<typename ...Ts> struct SOA_State { explicit SOA_State(size_t n):tuple_vecs_(std::vector<Ts>(n, Ts())...)//ok { } //using fold expresions for "," (comma) operator; requires c++17 // template<typename ...Fs, //pack of functor types std::size_t ...Is> //pack of tuple indices to fold over void transform_pack(const std::tuple<Fs...>& fctr_pack, //pack of fctrs std::tuple<std::vector<Ts>...>& result_pack, //pack of result vectors std::index_sequence<Is...>) { (std::transform(std::begin(std::get<Is>(tuple_vecs_)),std::end(std::get<Is>(tuple_vecs_)), std::begin(std::get<Is>(result_pack)),//std::back_inserter? std::get<Is>(fctr_pack)), ...); } //using fold expresions for "," (comma) operator; requires c++17 // template<typename ...Fs, //pack of functor types std::size_t ...Is> //pack of tuple indices to fold over static void foreach_pack(const std::tuple<Fs...>& fctr_pack, //pack of fctrs std::tuple<std::vector<Ts>...>& source_pack, //pack of result vectors std::index_sequence<Is...>) { (std::for_each(std::begin(std::get<Is>(source_pack)),std::end(std::get<Is>(source_pack)), std::get<Is>(fctr_pack)), ...); } private: std::tuple<std::vector<Ts>...> tuple_vecs_;//ok }; int main(void) { size_t n_states = 10; SOA_State<int, double, std::string> soa(n_states); auto fctr_tuple = std::make_tuple([](const int& v){return v+1;}, [](const double& v){return 2.0*(v+.13);}, [](const std::string& s){return std::string("hello");}); auto res_tuple = std::make_tuple(std::vector<int>(n_states,0), std::vector<double>(n_states,0.), std::vector<std::string>(n_states,std::string())); std::index_sequence_for<int, double, std::string> is; //apply transform on pack with results in another pack: // soa.transform_pack(fctr_tuple, res_tuple, is); auto printer_tuple = std::make_tuple([](const int& v){std::cout<<"i:"<<v<<"\n";}, [](const double& v){std::cout<<"d:"<<v<<"\n";}, [](const std::string& s){std::cout<<"s:"<<s<<"\n";}); //print the results pack: // SOA_State<int, double, std::string>::foreach_pack(printer_tuple, res_tuple, is); std::cout<<"\n"; std::cout<<"Done!"<<std::endl; };
source share