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