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::_N
(Nは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