fast_mem_fn.hpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // (C) Copyright Tobias Schwinger
  2. //
  3. // Use modification and distribution are subject to the boost Software License,
  4. // Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt).
  5. //------------------------------------------------------------------------------
  6. //
  7. // This example implements a very efficient, generic member function wrapper.
  8. //
  9. //
  10. // Detailed description
  11. // ====================
  12. //
  13. // For most platforms C++ runs on (maybe all hardware platforms, as opposed to
  14. // virtual machines) there are indirect calls that take more time to execute
  15. // than direct ones. Further calling a function usually takes more time than
  16. // inlining it at the call site.
  17. //
  18. // A direct call is a machine instruction that calls a subroutine at a known
  19. // address encoded in the instruction itself. C++ compilers usually emit one of
  20. // these instructions for each function call to a nonvirtual function (a call to
  21. // a virtual function requires either two direct calls or one indirect call).
  22. // An indirect call is a machine instruction that calls a subroutine at an
  23. // address known at runtime. C++ compilers usually emit at least one of these
  24. // instructions for a call through a callable builtin variable.
  25. //
  26. // It is possible to use callable scalars as non-type template arguments. This
  27. // way the compiler knows which function we want to call when generating the
  28. // code for the call site, so it may inline (if it decides to do so) or use a
  29. // direct call instead of being forced to use a slow, indirect call.
  30. //
  31. // We define a functor class template that encodes the function to call in its
  32. // type via a non-type template argument. Its (inline declared) overloaded
  33. // function call operator calls the function through that non-type template
  34. // argument. In the best case we end up inlining the callee directly at the
  35. // point of the call.
  36. //
  37. // Decomposition of the wrapped member function's type is needed in order to
  38. // implement argument forwarding (just using a templated call operator we would
  39. // encounter what is known as "the forwarding problem" [Dimov1]). Further we
  40. // can eliminate unecessary copies for each by-value parameter by using a
  41. // reference to its const qualified type for the corresponding parameter of the
  42. // wrapper's function call operator.
  43. //
  44. // Finally we provide a macro that does have similar semantics to the function
  45. // template mem_fn of the Bind [2] library.
  46. // We can't use a function template and use a macro instead, because we use a
  47. // member function pointer that is a compile time constant. So we first have to
  48. // deduce the type and create a template that accepts this type as a non-type
  49. // template argument, which is passed in in a second step. The macro hides this
  50. // lengthy expression from the user.
  51. //
  52. //
  53. // Limitations
  54. // ===========
  55. //
  56. // The "this-argument" must be specified as a reference.
  57. //
  58. //
  59. // Bibliography
  60. // ============
  61. //
  62. // [Dimov1] Dimov, P., Hinnant H., Abrahams, D. The Forwarding Problem
  63. // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm
  64. //
  65. // [Dimov2] Dimov, P. Documentation of boost::mem_fn
  66. // http://www.boost.org/libs/bind/mem_fn.html
  67. #ifndef BOOST_EXAMPLE_FAST_MEM_FN_HPP_INCLUDED
  68. #ifndef BOOST_PP_IS_ITERATING
  69. #include <boost/function_types/result_type.hpp>
  70. #include <boost/function_types/function_arity.hpp>
  71. #include <boost/function_types/parameter_types.hpp>
  72. #include <boost/function_types/is_member_function_pointer.hpp>
  73. #include <boost/mpl/transform_view.hpp>
  74. #include <boost/mpl/begin.hpp>
  75. #include <boost/mpl/next.hpp>
  76. #include <boost/mpl/deref.hpp>
  77. #include <boost/utility/enable_if.hpp>
  78. #include "detail/param_type.hpp"
  79. namespace example
  80. {
  81. namespace ft = boost::function_types;
  82. namespace mpl = boost::mpl;
  83. using namespace mpl::placeholders;
  84. // the functor class template
  85. template< typename MFPT, MFPT MemberFunction
  86. , size_t Arity = ::example::ft::function_arity<MFPT>::value
  87. >
  88. struct fast_mem_fn;
  89. // ------- ---- --- -- - - - -
  90. // deduce type and capture compile time value
  91. #define BOOST_EXAMPLE_FAST_MEM_FN(mfp) \
  92. ::example::make_fast_mem_fn(mfp).make_fast_mem_fn<mfp>()
  93. template<typename MFPT>
  94. struct fast_mem_fn_maker
  95. {
  96. template<MFPT Callee>
  97. fast_mem_fn<MFPT,Callee> make_fast_mem_fn()
  98. {
  99. return fast_mem_fn<MFPT,Callee>();
  100. }
  101. };
  102. template<typename MFPT>
  103. typename boost::enable_if<boost::is_member_function_pointer<MFPT>,
  104. fast_mem_fn_maker<MFPT> >::type
  105. make_fast_mem_fn(MFPT)
  106. {
  107. return fast_mem_fn_maker<MFPT>();
  108. }
  109. // ------- ---- --- -- - - - -
  110. namespace detail
  111. {
  112. // by-value forwarding optimization
  113. template<typename T>
  114. struct parameter_types
  115. : mpl::transform_view<ft::parameter_types<T>,param_type<_> >
  116. { };
  117. }
  118. // ------- ---- --- -- - - - -
  119. template< typename MFPT, MFPT MemberFunction >
  120. struct fast_mem_fn<MFPT, MemberFunction, 1>
  121. {
  122. // decompose the result and the parameter types (public for introspection)
  123. typedef typename ft::result_type<MFPT>::type result_type;
  124. typedef detail::parameter_types<MFPT> parameter_types;
  125. private:
  126. // iterate the parameter types
  127. typedef typename mpl::begin<parameter_types>::type i0;
  128. public:
  129. // forwarding function call operator
  130. result_type operator()( typename mpl::deref<i0>::type a0) const
  131. {
  132. return (a0.*MemberFunction)();
  133. };
  134. };
  135. template< typename MFPT, MFPT MemberFunction >
  136. struct fast_mem_fn<MFPT, MemberFunction, 2>
  137. {
  138. // decompose the result and the parameter types (public for introspection)
  139. typedef typename ft::result_type<MFPT>::type result_type;
  140. typedef detail::parameter_types<MFPT> parameter_types;
  141. private:
  142. // iterate the parameter types
  143. typedef typename mpl::begin<parameter_types>::type i0;
  144. typedef typename mpl::next<i0>::type i1;
  145. public:
  146. // forwarding function call operator
  147. result_type operator()( typename mpl::deref<i0>::type a0
  148. , typename mpl::deref<i1>::type a1) const
  149. {
  150. return (a0.*MemberFunction)(a1);
  151. };
  152. };
  153. template< typename MFPT, MFPT MemberFunction >
  154. struct fast_mem_fn<MFPT, MemberFunction, 3>
  155. {
  156. // decompose the result and the parameter types (public for introspection)
  157. typedef typename ft::result_type<MFPT>::type result_type;
  158. typedef detail::parameter_types<MFPT> parameter_types;
  159. private:
  160. // iterate the parameter types
  161. typedef typename mpl::begin<parameter_types>::type i0;
  162. typedef typename mpl::next<i0>::type i1;
  163. typedef typename mpl::next<i1>::type i2;
  164. public:
  165. // forwarding function call operator
  166. result_type operator()( typename mpl::deref<i0>::type a0
  167. , typename mpl::deref<i1>::type a1
  168. , typename mpl::deref<i2>::type a2) const
  169. {
  170. return (a0.*MemberFunction)(a1,a2);
  171. };
  172. };
  173. // ...
  174. }
  175. // ------- ---- --- -- - - - -
  176. // preprocessor-based code generator to continue the repetitive part, above
  177. #include <boost/preprocessor/cat.hpp>
  178. #include <boost/preprocessor/arithmetic/inc.hpp>
  179. #include <boost/preprocessor/iteration/iterate.hpp>
  180. #include <boost/preprocessor/iteration/local.hpp>
  181. #include <boost/preprocessor/repetition/enum_shifted_params.hpp>
  182. #include <boost/preprocessor/repetition/enum_binary_params.hpp>
  183. namespace example
  184. {
  185. #if BOOST_FT_MAX_ARITY >= 4
  186. # define BOOST_PP_FILENAME_1 "fast_mem_fn.hpp"
  187. # define BOOST_PP_ITERATION_LIMITS (4,BOOST_FT_MAX_ARITY)
  188. # include BOOST_PP_ITERATE()
  189. #endif
  190. }
  191. #define BOOST_EXAMPLE_FAST_MEM_FN_HPP_INCLUDED
  192. #else
  193. #define N BOOST_PP_FRAME_ITERATION(1)
  194. template< typename MFPT, MFPT MemberFunction >
  195. struct fast_mem_fn<MFPT, MemberFunction, N >
  196. {
  197. // decompose the result and the parameter types (public for introspection)
  198. typedef typename ft::result_type<MFPT>::type result_type;
  199. typedef detail::parameter_types<MFPT> parameter_types;
  200. private:
  201. // iterate the parameter types
  202. typedef typename mpl::begin<parameter_types>::type i0;
  203. #define BOOST_PP_LOCAL_LIMITS (0,N-2)
  204. #define BOOST_PP_LOCAL_MACRO(j) \
  205. typedef typename mpl::next< i ## j >::type BOOST_PP_CAT(i,BOOST_PP_INC(j)) ;
  206. #include BOOST_PP_LOCAL_ITERATE()
  207. public:
  208. // forwarding function call operator
  209. result_type operator()(
  210. BOOST_PP_ENUM_BINARY_PARAMS(N, typename mpl::deref<i,>::type a) ) const
  211. {
  212. return (a0.*MemberFunction)(BOOST_PP_ENUM_SHIFTED_PARAMS(N,a));
  213. };
  214. };
  215. #undef N
  216. #endif
  217. #endif