BoostML - 【function/bind】 function object type introspection
template <class F> void foo(F f) { boost::function<???> ff = f; }
みたいなことがしたいんだけど、どうすればいいの、というお話。
MLではBoost.FunctionTypesで関数(オブジェクト)から
- 戻り値の型:result_type
- パラメータパック:parameter_types
で取り出せばいいんじゃない、という回答がついてますが、
parameter_typesが返すのはMPLのシーケンスなので、それを展開するのはなかなか難しかったりします。
先のエントリで作成したfusion::vectorからstd::tupleへ変換するfuvector_to_tupleメタ関数を使用して
MPLのシーケンスをstd::tupleに変換し、可変引数テンプレートで型を取り出してみました。
#include <iostream> #include <tuple> #include <boost/function.hpp> #include <boost/type_traits.hpp> #include <boost/mpl/int.hpp> #include <boost/mpl/pop_front.hpp> #include <boost/function_types/result_type.hpp> #include <boost/function_types/parameter_types.hpp> #include <boost/fusion/include/vector.hpp> #include <boost/fusion/include/value_at.hpp> #include <boost/fusion/include/size.hpp> #include <boost/fusion/include/pop_front.hpp> #include <boost/fusion/include/as_vector.hpp> #include <boost/fusion/adapted/mpl.hpp> template <class List, class FVector, int N> struct fuvector_to_tuple_impl; template <class... Args, class FVector, int N> struct fuvector_to_tuple_impl<std::tuple<Args...>, FVector, N> { typedef typename fuvector_to_tuple_impl< std::tuple<Args..., typename boost::fusion::result_of::value_at<FVector, boost::mpl::int_<0>>::type>, typename boost::fusion::result_of::pop_front<FVector>::type, N - 1 >::type type; }; template <class... Args, class FVector> struct fuvector_to_tuple_impl<std::tuple<Args...>, FVector, 0> { typedef boost::tuple<Args...> type; }; template <class FVector> struct fuvector_to_tuple { typedef typename fuvector_to_tuple_impl< std::tuple<>, FVector, boost::fusion::result_of::size<FVector>::value >::type type; }; template <class Mask> struct get_parameter; template <class Vector, int Size> struct get_parameter<boost::mpl::v_mask<Vector, Size>> { typedef typename boost::mpl::pop_front<Vector>::type type; }; template <class R, class Args> struct to_function_impl; template <class R, class... Args> struct to_function_impl<R, std::tuple<Args...>> { typedef std::function<R(Args...)> type; }; template <class F> struct to_function { private: typedef typename boost::function_types::result_type<F>::type result_type; typedef typename boost::function_types::parameter_types<F>::type parameter; typedef typename get_parameter<parameter>::type masked_types; typedef typename boost::fusion::result_of::as_vector<masked_types>::type vector_parameter; typedef typename fuvector_to_tuple<vector_parameter>::type parameter_types; public: typedef typename to_function_impl<result_type, parameter_types>::type type; }; template <class F> void foo(F f) { typedef typename to_function<F>::type function_type; static_assert(boost::is_same<function_type, boost::function<void(int)>>::value, "not same"); } void a(int) {} int main() { foo(a); }
追記:
2009/10/06 16:37 間違ったコードを掲載してしまっていたので、差し替えました。
追記2:
boost::function_types::function_typeを使うと
mpl::vectorから関数型を作れるそうなので、これでいいみたいです。
#include <boost/static_assert.hpp> #include <boost/function.hpp> #include <boost/type_traits.hpp> #include <boost/function_types/function_type.hpp> #include <boost/function_types/parameter_types.hpp> template <class Mask> struct get_parameter; template <class Vector, int Size> struct get_parameter<boost::mpl::v_mask<Vector, Size> > { typedef Vector type; }; template <class F> struct to_function { private: typedef typename boost::function_types::function_type< typename get_parameter< typename boost::function_types::parameter_types<F>::type >::type >::type signature; public: typedef boost::function<signature> type; }; template <class F> void foo(F f) { typedef typename to_function<F>::type function_type; BOOST_STATIC_ASSERT((boost::is_same<function_type, boost::function<void(int)> >::value)); } void a(int) {} int main() { foo(a); }
ついでに、可変引数テンプレートが必要なくなったので
C++03のコードにしました。
追記3:
まだ間違ってたっぽい。
v_maskはBOOST_MPL_CFG_TYPEOF_BASED_SEQUENCESがdefineされてるときのみ定義されるので
直接使ってはいけない(by id:uskz)のと、VC9でやったらv_maskがないと言われてエラーになり
ごにょごにょいじってたらこうなりました。
#include <boost/static_assert.hpp> #include <boost/function.hpp> #include <boost/type_traits.hpp> #include <boost/mpl/push_front.hpp> #include <boost/function_types/function_type.hpp> #include <boost/function_types/result_type.hpp> #include <boost/function_types/parameter_types.hpp> template <class F> struct to_function { private: typedef typename boost::function_types::result_type<F>::type result_type; typedef typename boost::function_types::parameter_types<F>::type parameters; typedef typename boost::mpl::push_front<parameters, result_type>::type pack; typedef typename boost::function_types::function_type<pack>::type signature; public: typedef boost::function<signature> type; }; template <class F> void foo(F f) { typedef typename to_function<F>::type function_type; BOOST_STATIC_ASSERT((boost::is_same<function_type, boost::function<void(int)> >::value)); } void a(int) {} int main() { foo(a); }
追記4:
boost::function_types::componentsを使って最終的にこうなりました。
#include <boost/static_assert.hpp> #include <boost/function.hpp> #include <boost/type_traits.hpp> #include <boost/function_types/function_type.hpp> #include <boost/function_types/components.hpp> template <class F> struct to_function { private: typedef typename boost::function_types::components<F>::type components; typedef typename boost::function_types::function_type<components>::type signature; public: typedef boost::function<signature> type; }; template <class F> void foo(F f) { typedef typename to_function<F>::type function_type; BOOST_STATIC_ASSERT((boost::is_same<function_type, boost::function<void(int)> >::value)); } void a(int) {} int main() { foo(a); }