N個のplaceholderを作って展開する

はじめに

任意の関数型変数を引き数として受取り、
第1引数を1にbindしてretrunする方法に関してのメモです

template<typename R, typename A,typename... Args>
auto bind_1_1(std::function<R(A,Args...) f>
{
    return //std::bind(f,1,make_placeholders<Args...>{}...); //理想イメージ
}


int main()
{
    std::function<int(int,int,int,int)> f = [](int x,int y,int z,int w){return x+y+z+w;}
    std::function<int(int.int,int)>     g = bind_1_1(f) //=> [](int y,int z,int w){return 1+y+z+w;}
}

こんなの

独自のplaceholder

標準ライブラリのほうでstd::placeholder::_NNは1,2,3,...)などが用意されています
std::bindは関数のすべて引数を変数・定数で束縛するために使用されますが
std::placeholder::_Nを使うと,引数を引数で束縛する事が可能になります.

std::function<int(int,int)> f = [](int x,int y){ return x+y; }
auto g = std::bind(f,3,5)                     //=> [](     ){ return 3+5; }
auto h = std::bind(f,3,std::placeholder::_1); //=> [](int i){ return 3+i; }

最初に行う準備として non-template parameter(ようはint)引数で整数を受取れる
class template(構造体)を定義し、std::placehoderと関連付けをします

//独自のplaceholder
namespace my{
    template<int N>
    struct placehoder{};
} 
namespace std {
    template<int N>
    struct is_placeholder<my::placeholder<N>> : public integral_constant<int, N> {};
}

これをやると例えばmy::placeholder<1>std::placeholder::_1
同じ働きをするようになります

数値Nを受取り、1~Nまでの数列の生成する

これは簡単ですね

//Ns...をただ包むだけの型
template<int... Ns>
struct num_pack
{};
// num_pack<1,2,3,...>という用に定数リストを持てる


template<int N,int Ns...>
struct num_pack_impl //otherwise
{
  typedef typename num_pack_impl<N-1,N,Ns...>::type type;
};
template<int... Ns>
struct num_pack_impl<1,Ns...> //<1,...>
{
  typedef num_pack<1,Ns...> type;
}

template<typename... Args>
using  num_sequence = typename num_pack_impl<sizeof...(Args)>::type; // num_pack<1,2,...,(sizeof...(Args))>

これで 例えばnum_sequence<int,int,int,int,int>num_pack_impl<5>::typeとなり
num_pack<1,2,3,4,5>になります

placeholdersの作成

上記を利用して作ります

//
template<typename R, typename A,typename... Args,int... Ns>
auto bind_1_1_impl(std::function<R(A,Args...)> f, num_pack<Ns...> )
{
    return std::bind( f, 1, placeholder<Ns>{}... );
}

//ユーザーが呼び出すほうの関数
template<typename R, typename A,typename... Args>
auto bind_1_1(std::function<R(A,Args...) > f>
{
    return bind_1_1_impl( f, num_pack_impl<Args...>{} ); //bind_1_1impleを呼び出す
}

ここで,my::placeholder<Ns>...と言うものがありますが,Ns...=1,2,3の場合
my::placeholder<Ns>...my::placeholder<1>,my::placeholder<2>,my::placeholder<3> となります. また,<Args...>からnum_sequence関数を使って<Ns...>を作る事は出来ますが <Ns...>型からNs...型を取り出してmy::placeholderの型引数などに利用するためには, 補助関数bind_1_1_implを用意してやる必要があります

まとめ

上記をまとめると以下のようになります

#include <iostream>
#include <functional>

//独自のplaceholder
namespace my{
    template<int N>
    struct placeholder{};
}
namespace std {
    template<int N>
    struct is_placeholder<my::placeholder<N>> : public integral_constant<int, N> {};
}

template<int... Ns>
struct num_pack
{};

template<int N,int... Ns>
struct num_sequence
{
  typedef typename num_sequence<N-1,N,Ns...>::type type;
};

template<int... Ns>
struct num_sequence<1,Ns...>
{
  typedef num_pack<1,Ns...> type;
};

template<typename... Args>
using num_pack_impl_= typename num_sequence<sizeof...(Args)>::type;

template<typename R, typename A,typename... Args,int... Ns>
auto bind_1_1(std::function<R(A,Args...)> f, num_pack<Ns...> )
{
    return std::bind( f, 1, my::placeholder<Ns>{}... );
}

// userが使用するほう
template<typename R, typename A,typename... Args>
auto bind_1_1(std::function<R(A,Args...)> f)
{
    return bind_1_1(f,num_pack_impl_<Args...>{});
}
            
int main(){
    std::function<int(int,int,int,int)> f1 = [](int a,int b,int c,int d){return a+b+c+d;};
    auto a= bind_1_1(f1);
    std::cout<< a(1000,100,10) <<std::endl;
}

http://melpon.org/wandbox/permlink/5NfIBcn0ZjDjN1vz

参考:
http://stackoverflow.com/questions/21192659/variadic-templates-and-stdbind
https://gist.github.com/anonymous/400983