在 std::tuple<...> 上实现 map() 和 each() - 将索引作为模板参数传递给仿函数
Implementing map() and each() over std::tuple<...> - with index passed to functor as template parameter
经过几年的 Web 开发,我再次使用 C++ (14) 并决定使用一些 "dynamically typed functional fun" 模板元编程。
我已经在元组上实现了 map
和 each
:
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());
}
以及 map
的类似实现。
案例 #1 通过语法检查(我还没有尝试 运行)。
案例 #2 也通过了语法检查,tuple_each_internal
被修改为将索引作为函数参数传递:func(index, get<index>(tuple))
.
Case #3 优于 Case #2,因为 index
的值可以在编译时传递给其他模板(例如 get<index>(tuple)
),这不是情况 #2 可能。
我无法实施案例 #3。
鉴于回调签名:
template <typename T, size_t index>
void operator ()(T&& t) { ; }
我试过这个 tuple_each_internal
:
auto res = {
(func<typename tuple_element<index, Tuple>::type, index>(get<index>(tuple)), nullptr)...
};
Clang++ 3.6.1 输出:
$ 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 输出:
$ 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)... };
^
应该是:
auto res = {
(func.template operator()<const typename tuple_element<index, Tuple>::type&, index>(get<index>(tuple)),
nullptr)...
};
使用
更简单
/* Case #4: "Each with index as compile-time parameter" callback */
template <size_t index, typename T>
void operator ()(T&& t);
代码变为:
auto res = {
(func.template operator()<index>(get<index>(tuple)),
nullptr)...
};
甚至
template <size_t N, typename T>
void operator ()(std::integral_constant<std::size_t, N>, T&& t);
这导致
auto res = {
(func(std::integral_constant<std::size_t, index>{}, get<index>(tuple)),
nullptr)...
};
这里有一个在 C++17 中做类似事情的例子,在逗号运算符上使用折叠表达式:
//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;
};
经过几年的 Web 开发,我再次使用 C++ (14) 并决定使用一些 "dynamically typed functional fun" 模板元编程。
我已经在元组上实现了 map
和 each
:
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());
}
以及 map
的类似实现。
案例 #1 通过语法检查(我还没有尝试 运行)。
案例 #2 也通过了语法检查,
tuple_each_internal
被修改为将索引作为函数参数传递:func(index, get<index>(tuple))
.Case #3 优于 Case #2,因为
index
的值可以在编译时传递给其他模板(例如get<index>(tuple)
),这不是情况 #2 可能。
我无法实施案例 #3。
鉴于回调签名:
template <typename T, size_t index>
void operator ()(T&& t) { ; }
我试过这个 tuple_each_internal
:
auto res = {
(func<typename tuple_element<index, Tuple>::type, index>(get<index>(tuple)), nullptr)...
};
Clang++ 3.6.1 输出:
$ 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 输出:
$ 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)... };
^
应该是:
auto res = {
(func.template operator()<const typename tuple_element<index, Tuple>::type&, index>(get<index>(tuple)),
nullptr)...
};
使用
更简单/* Case #4: "Each with index as compile-time parameter" callback */
template <size_t index, typename T>
void operator ()(T&& t);
代码变为:
auto res = {
(func.template operator()<index>(get<index>(tuple)),
nullptr)...
};
甚至
template <size_t N, typename T>
void operator ()(std::integral_constant<std::size_t, N>, T&& t);
这导致
auto res = {
(func(std::integral_constant<std::size_t, index>{}, get<index>(tuple)),
nullptr)...
};
这里有一个在 C++17 中做类似事情的例子,在逗号运算符上使用折叠表达式:
//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;
};