在 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" 模板元编程。

我已经在元组上实现了 mapeach

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 的类似实现。

我无法实施案例 #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)...
};

Live Demo.

使用

更简单
/* 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)...
};

Live Demo

甚至

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

Live Demo

这里有一个在 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;
};