C++0x で make_fused を書く
Variadic Templates を使うことによって、驚くほど簡潔に書くことができます (Egg に合わせて fuse という名前にしています)。
#include <cstddef> #include <tuple> #include <type_traits> #include <utility> namespace fuse_detail { template <std::size_t ...Indexes> struct index_tuple { typedef index_tuple<Indexes..., sizeof...(Indexes)> next; }; template <std::size_t N> struct build_index_tuple { typedef typename build_index_tuple<N - 1>::type::next type; }; template <> struct build_index_tuple<0> { typedef index_tuple<> type; }; template <class F, class Args, std::size_t ...Indexes> auto call(F const &f, Args &&args, index_tuple<Indexes...>) -> decltype(f(std::get<Indexes>(std::forward<Args>(args))...)) { return f(std::get<Indexes>(std::forward<Args>(args))...); } } template <class F> struct fused { F f; template < class Args, class Indexes = typename fuse_detail::build_index_tuple< std::tuple_size<typename std::remove_reference<Args>::type>::value >::type > auto operator()(Args &&args) const -> decltype(fuse_detail::call(f, std::forward<Args>(args), Indexes())) { return fuse_detail::call(f, std::forward<Args>(args), Indexes()); } }; template <class F> inline fused<typename std::decay<F>::type> fuse(F &&f) { return { std::forward<F>(f) }; }
使い方
int unfused_plus(int i, int j, int k) { return i + j + k; } int main() { std::cout << fuse(&unfused_plus)(std::forward_as_tuple(1, 2, 3)); // 6 }
タプル*1として渡された引数をいかに関数に forward するかが問題となるわけですが、それには GCC の pair piecewise construction の実装が参考になります。すなわち、引数のタプル Args &&args が与えられた場合、
- std::tuple_size によって引数の数 N を求める
- Variadic な列 std::size_t ...Indexes = 0, 1, 2, ..., N - 1 を生成する
- std::get
(std::forward (args))... を目的の関数に渡す
ことによって forward を行うことができます。
Variadic Templates の力がよくわかる例でした。
*1:std::tuple_size と std::get を特殊化すれば、Fusion の列も使えるようになります