1 /*******************************************************************************
2 * tlx/delegate.hpp
3 *
4 * Replacement for std::function with ideas and base code borrowed from
5 * http://codereview.stackexchange.com/questions/14730/impossibly-fast-delegate
6 * Massively rewritten, commented, simplified, and improved.
7 *
8 * Part of tlx - http://panthema.net/tlx
9 *
10 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
11 *
12 * All rights reserved. Published under the Boost Software License, Version 1.0
13 ******************************************************************************/
14
15 #ifndef TLX_DELEGATE_HEADER
16 #define TLX_DELEGATE_HEADER
17
18 #include <algorithm>
19 #include <cassert>
20 #include <cstddef>
21 #include <memory>
22 #include <type_traits>
23 #include <utility>
24
25 namespace tlx {
26
27 template <typename T, typename Allocator = std::allocator<void> >
28 class Delegate;
29
30 /*!
31 * This is a faster replacement than std::function. Besides being faster and
32 * doing less allocations when used correctly, we use it in places where
33 * move-only lambda captures are necessary. std::function is required by the
34 * standard to be copy-constructible, and hence does not allow move-only
35 * lambda captures.
36 *
37 * A Delegate contains a reference to any of the following callable objects:
38 * - an immediate function (called via one indirection)
39 * - a mutable function pointer (copied into the Delegate)
40 * - an immediate class::method call (called via one indirection)
41 * - a functor object (the whole object is copied into the Delegate)
42 *
43 * All callable objects must have the signature ReturnType(Arguments ...). If a
44 * callable has this signature, it can be bound to the Delegate.
45 *
46 * To implement all this the Delegate contains one pointer to a "caller stub"
47 * function, which depends on the contained object and can be an immediate
48 * function call, a pointer to the object associated with the callable, and a
49 * memory pointer (managed by shared_ptr) for holding larger callables that need
50 * to be copied.
51 *
52 * A functor object can be a lambda function with its capture, an internally
53 * wrapped mutable class::method class stored as pair<object, method_ptr>, or
54 * any other old-school functor object.
55 *
56 * Delegates can be constructed similar to std::function.
57 \code
58 // in defining the Delegate we decide the ReturnType(Arguments ...) signature
59 using MyDelegate = Delegate<int(double)>;
60
61 // this is a plain function bound to the Delegate as a function pointer
62 int func(double a) { return a + 10; }
63 MyDelegate d1 = MyDelegate(func);
64
65 class AClass {
66 public:
67 int method(double d) { return d * d; }
68 };
69
70 AClass a;
71
72 // this is class::method bound to the Delegate via indirection, warning: this
73 // creates a needless allocation, because it is stored as pair<Class,Method>
74 MyDelegate d2 = MyDelegate(a, &AClass::method);
75 // same as above
76 MyDelegate d3 = MyDelegate::make(a, &AClass::method);
77
78 // class::method bound to the Delegate via instantiation of an immediate caller
79 // to the method AClass::method. this is preferred and does not require any
80 // memory allocation!
81 MyDelegate d4 = MyDelegate::make<AClass, &AClass::method>(a);
82
83 // a lambda with capture bound to the Delegate, this always performs a memory
84 // allocation to copy the capture closure.
85 double offset = 42.0;
86 MyDelegate d5 = [&](double a) { return a + offset; };
87 \endcode
88 *
89 */
90 template <typename R, typename... A, typename Allocator>
91 class Delegate<R(A...), Allocator>
92 {
93 public:
94 //! default constructor
95 Delegate() = default;
96
97 //! copy constructor
98 Delegate(const Delegate&) = default;
99
100 //! move constructor
101 Delegate(Delegate&&) = default;
102
103 //! copy assignment operator
104 Delegate& operator = (const Delegate&) = default;
105
106 //! move assignment operator
107 Delegate& operator = (Delegate&&) = default;
108
109 //! \name Immediate Function Calls
110 //! \{
111
112 //! construction from an immediate function with no object or pointer.
113 template <R(* const Function)(A...)>
make()114 static Delegate make() noexcept {
115 return Delegate(function_caller<Function>, nullptr);
116 }
117
118 //! \}
119
120 //! \name Function Pointer Calls
121 //! \{
122
123 //! constructor from a plain function pointer with no object.
Delegate(R (* const function_ptr)(A...))124 explicit Delegate(R(*const function_ptr)(A...)) noexcept
125 : Delegate(function_ptr_caller,
126 *reinterpret_cast<void* const*>(&function_ptr)) { }
127
128 static_assert(sizeof(void*) == sizeof(void (*)(void)),
129 "object pointer and function pointer sizes must equal");
130
131 //! construction from a plain function pointer with no object.
make(R (* const function_ptr)(A...))132 static Delegate make(R(*const function_ptr)(A...)) noexcept {
133 return Delegate(function_ptr);
134 }
135
136 //! \}
137
138 //! \name Immediate Class::Method Calls with Objects
139 //! \{
140
141 //! construction for an immediate class::method with class object
142 template <class C, R(C::* const Method)(A...)>
make(C * const object_ptr)143 static Delegate make(C* const object_ptr) noexcept {
144 return Delegate(method_caller<C, Method>, object_ptr);
145 }
146
147 //! construction for an immediate class::method with class object
148 template <class C, R(C::* const Method)(A...) const>
make(C const * const object_ptr)149 static Delegate make(C const* const object_ptr) noexcept {
150 return Delegate(const_method_caller<C, Method>,
151 const_cast<C*>(object_ptr));
152 }
153
154 //! construction for an immediate class::method with class object by
155 //! reference
156 template <class C, R(C::* const Method)(A...)>
make(C & object)157 static Delegate make(C& object) noexcept {
158 return Delegate(method_caller<C, Method>, &object);
159 }
160
161 //! construction for an immediate class::method with class object by
162 //! reference
163 template <class C, R(C::* const Method)(A...) const>
make(C const & object)164 static Delegate make(C const& object) noexcept {
165 return Delegate(const_method_caller<C, Method>,
166 const_cast<C*>(&object));
167 }
168
169 //! \}
170
171 //! \name Lambdas with Captures and Wrapped Class::Method Calls with Objects
172 //! \{
173
174 //! constructor from any functor object T, which may be a lambda with
175 //! capture or a MemberPair or ConstMemberPair wrapper.
176 template <
177 typename T,
178 typename = typename std::enable_if<
179 !std::is_same<Delegate, typename std::decay<T>::type>::value
180 >::type
181 >
Delegate(T && f)182 Delegate(T&& f)
183 : store_(
184 // allocate memory for T in shared_ptr with appropriate deleter
185 typename Allocator::template rebind<
186 typename std::decay<T>::type>::other().allocate(1),
187 store_deleter<typename std::decay<T>::type>, Allocator()) {
188
189 using Functor = typename std::decay<T>::type;
190 using Rebind = typename Allocator::template rebind<Functor>::other;
191
192 // copy-construct T into shared_ptr memory.
193 Rebind().construct(
194 static_cast<Functor*>(store_.get()), Functor(std::forward<T>(f)));
195
196 object_ptr_ = store_.get();
197
198 caller_ = functor_caller<Functor>;
199 }
200
201 //! constructor from any functor object T, which may be a lambda with
202 //! capture or a MemberPair or ConstMemberPair wrapper.
203 template <typename T>
make(T && f)204 static Delegate make(T&& f) {
205 return std::forward<T>(f);
206 }
207
208 //! constructor for wrapping a class::method with object pointer.
209 template <class C>
Delegate(C * const object_ptr,R (C::* const method_ptr)(A...))210 Delegate(C* const object_ptr, R(C::* const method_ptr)(A...))
211 : Delegate(MemberPair<C>(object_ptr, method_ptr)) { }
212
213 //! constructor for wrapping a const class::method with object pointer.
214 template <class C>
Delegate(C * const object_ptr,R (C::* const method_ptr)(A...)const)215 Delegate(C* const object_ptr, R(C::* const method_ptr)(A...) const)
216 : Delegate(ConstMemberPair<C>(object_ptr, method_ptr)) { }
217
218 //! constructor for wrapping a class::method with object reference.
219 template <class C>
Delegate(C & object,R (C::* const method_ptr)(A...))220 Delegate(C& object, R(C::* const method_ptr)(A...))
221 : Delegate(MemberPair<C>(&object, method_ptr)) { }
222
223 //! constructor for wrapping a const class::method with object reference.
224 template <class C>
Delegate(C const & object,R (C::* const method_ptr)(A...)const)225 Delegate(C const& object, R(C::* const method_ptr)(A...) const)
226 : Delegate(ConstMemberPair<C>(&object, method_ptr)) { }
227
228 //! constructor for wrapping a class::method with object pointer.
229 template <class C>
make(C * const object_ptr,R (C::* const method_ptr)(A...))230 static Delegate make(C* const object_ptr,
231 R(C::* const method_ptr)(A...)) {
232 return MemberPair<C>(object_ptr, method_ptr);
233 }
234
235 //! constructor for wrapping a const class::method with object pointer.
236 template <class C>
make(C const * const object_ptr,R (C::* const method_ptr)(A...)const)237 static Delegate make(C const* const object_ptr,
238 R(C::* const method_ptr)(A...) const) {
239 return ConstMemberPair<C>(object_ptr, method_ptr);
240 }
241
242 //! constructor for wrapping a class::method with object reference.
243 template <class C>
make(C & object,R (C::* const method_ptr)(A...))244 static Delegate make(C& object, R(C::* const method_ptr)(A...)) {
245 return MemberPair<C>(&object, method_ptr);
246 }
247
248 //! constructor for wrapping a const class::method with object reference.
249 template <class C>
make(C const & object,R (C::* const method_ptr)(A...)const)250 static Delegate make(C const& object,
251 R(C::* const method_ptr)(A...) const) {
252 return ConstMemberPair<C>(&object, method_ptr);
253 }
254
255 //! \}
256
257 //! \name Miscellaneous
258 //! \{
259
260 //! reset delegate to invalid.
reset()261 void reset() { caller_ = nullptr; store_.reset(); }
262
reset_caller()263 void reset_caller() noexcept { caller_ = nullptr; }
264
265 //! swap delegates
swap(Delegate & other)266 void swap(Delegate& other) noexcept { std::swap(*this, other); }
267
268 //! compare delegate with another
operator ==(Delegate const & rhs) const269 bool operator == (Delegate const& rhs) const noexcept {
270 return (object_ptr_ == rhs.object_ptr_) && (caller_ == rhs.caller_);
271 }
272
273 //! compare delegate with another
operator !=(Delegate const & rhs) const274 bool operator != (Delegate const& rhs) const noexcept {
275 return !operator == (rhs);
276 }
277
278 //! compare delegate with another
operator <(Delegate const & rhs) const279 bool operator < (Delegate const& rhs) const noexcept {
280 return (object_ptr_ < rhs.object_ptr_) ||
281 ((object_ptr_ == rhs.object_ptr_) && (caller_ < rhs.caller_));
282 }
283
284 //! compare delegate with another
operator ==(std::nullptr_t const) const285 bool operator == (std::nullptr_t const) const noexcept {
286 return caller_ == nullptr;
287 }
288
289 //! compare delegate with another
operator !=(std::nullptr_t const) const290 bool operator != (std::nullptr_t const) const noexcept {
291 return caller_ != nullptr;
292 }
293
294 //! explicit conversion to bool -> valid or invalid.
operator bool() const295 explicit operator bool () const noexcept { return caller_ != nullptr; }
296
297 //! most important method: call. The call is forwarded to the selected
298 //! function caller.
operator ()(A...args) const299 R operator () (A... args) const {
300 assert(caller_);
301 return caller_(object_ptr_, std::forward<A>(args) ...);
302 }
303
304 //! \}
305
306 private:
307 //! type of the function caller pointer.
308 using Caller = R (*)(void*, A&& ...);
309
310 using Deleter = void (*)(void*);
311
312 //! pointer to function caller which depends on the type in object_ptr_. The
313 //! caller_ contains a plain pointer to either function_caller, a
314 //! function_ptr_caller, a method_caller, a const_method_caller, or a
315 //! functor_caller.
316 Caller caller_ = nullptr;
317
318 //! pointer to object held by the delegate: for plain function pointers it
319 //! is the function pointer, for class::methods it is a pointer to the class
320 //! instance, for functors it is a pointer to the shared_ptr store_
321 //! contents.
322 void* object_ptr_ = nullptr;
323
324 //! shared_ptr used to contain a memory object containing the callable, like
325 //! lambdas with closures, or our own wrappers.
326 std::shared_ptr<void> store_;
327
328 //! private constructor for plain
Delegate(const Caller & m,void * const obj)329 Delegate(const Caller& m, void* const obj) noexcept
330 : caller_(m), object_ptr_(obj) { }
331
332 //! deleter for stored functor closures
333 template <typename T>
store_deleter(void * const ptr)334 static void store_deleter(void* const ptr) {
335 using Rebind = typename Allocator::template rebind<T>::other;
336
337 Rebind().destroy(static_cast<T*>(ptr));
338 Rebind().deallocate(static_cast<T*>(ptr), 1);
339 }
340
341 //! \name Callers for simple function and immediate class::method calls.
342 //! \{
343
344 //! caller for an immediate function with no object or pointer.
345 template <R(* Function)(A...)>
function_caller(void * const,A &&...args)346 static R function_caller(void* const, A&& ... args) {
347 return Function(std::forward<A>(args) ...);
348 }
349
350 //! caller for a plain function pointer.
function_ptr_caller(void * const object_ptr,A &&...args)351 static R function_ptr_caller(void* const object_ptr, A&& ... args) {
352 return (*reinterpret_cast<R(* const*)(A...)>(&object_ptr))(args...);
353 }
354
355 //! function caller for immediate class::method function calls
356 template <class C, R(C::* method_ptr)(A...)>
method_caller(void * const object_ptr,A &&...args)357 static R method_caller(void* const object_ptr, A&& ... args) {
358 return (static_cast<C*>(object_ptr)->*method_ptr)(
359 std::forward<A>(args) ...);
360 }
361
362 //! function caller for immediate const class::method functions calls.
363 template <class C, R(C::* method_ptr)(A...) const>
const_method_caller(void * const object_ptr,A &&...args)364 static R const_method_caller(void* const object_ptr, A&& ... args) {
365 return (static_cast<C const*>(object_ptr)->*method_ptr)(
366 std::forward<A>(args) ...);
367 }
368
369 //! \}
370
371 //! \name Wrappers for indirect class::method calls.
372 //! \{
373
374 //! wrappers for indirect class::method calls containing (object,
375 //! method_ptr)
376 template <class C>
377 using MemberPair =
378 std::pair<C* const, R(C::* const)(A...)>;
379
380 //! wrappers for indirect const class::method calls containing (object,
381 //! const method_ptr)
382 template <class C>
383 using ConstMemberPair =
384 std::pair<C const* const, R(C::* const)(A...) const>;
385
386 //! template for class::function selector
387 template <typename>
388 struct IsMemberPair : std::false_type { };
389
390 //! specialization for class::function selector
391 template <class C>
392 struct IsMemberPair<MemberPair<C> >: std::true_type { };
393
394 //! template for const class::function selector
395 template <typename>
396 struct IsConstMemberPair : std::false_type { };
397
398 //! specialization for const class::function selector
399 template <class C>
400 struct IsConstMemberPair<ConstMemberPair<C> >: std::true_type { };
401
402 //! function caller for functor class.
403 template <typename T>
404 static typename std::enable_if<
405 !(IsMemberPair<T>::value || IsConstMemberPair<T>::value), R
406 >::type
functor_caller(void * const object_ptr,A &&...args)407 functor_caller(void* const object_ptr, A&& ... args) {
408 return (*static_cast<T*>(object_ptr))(std::forward<A>(args) ...);
409 }
410
411 //! function caller for const functor class.
412 template <typename T>
413 static typename std::enable_if<
414 (IsMemberPair<T>::value || IsConstMemberPair<T>::value), R
415 >::type
functor_caller(void * const object_ptr,A &&...args)416 functor_caller(void* const object_ptr, A&& ... args) {
417 return (static_cast<T*>(object_ptr)->first->*
418 static_cast<T*>(object_ptr)->second)(std::forward<A>(args) ...);
419 }
420
421 //! \}
422 };
423
424 //! make template alias due to similarity with std::function
425 template <typename T, typename Allocator = std::allocator<void> >
426 using delegate = Delegate<T, Allocator>;
427
428 //! constructor for wrapping a class::method with object pointer.
429 template <class C, typename R, typename... A>
430 inline Delegate<R(A...)>
make_delegate(C * const object_ptr,R (C::* const method_ptr)(A...))431 make_delegate(
432 C* const object_ptr, R(C::* const method_ptr)(A...)) noexcept {
433 return Delegate<R(A...)>::template make<C>(object_ptr, method_ptr);
434 }
435
436 //! constructor for wrapping a const class::method with object pointer.
437 template <class C, typename R, typename... A>
438 inline Delegate<R(A...)>
make_delegate(C * const object_ptr,R (C::* const method_ptr)(A...)const)439 make_delegate(
440 C* const object_ptr, R(C::* const method_ptr)(A...) const) noexcept {
441 return Delegate<R(A...)>::template make<C>(object_ptr, method_ptr);
442 }
443
444 //! constructor for wrapping a class::method with object reference.
445 template <class C, typename R, typename... A>
446 inline Delegate<R(A...)>
make_delegate(C & object_ptr,R (C::* const method_ptr)(A...))447 make_delegate(
448 C& object_ptr, R(C::* const method_ptr)(A...)) noexcept { // NOLINT
449 return Delegate<R(A...)>::template make<C>(object_ptr, method_ptr);
450 }
451
452 //! constructor for wrapping a const class::method with object reference.
453 template <class C, typename R, typename... A>
454 inline Delegate<R(A...)>
make_delegate(C const & object_ptr,R (C::* const method_ptr)(A...)const)455 make_delegate(
456 C const& object_ptr, R(C::* const method_ptr)(A...) const) noexcept {
457 return Delegate<R(A...)>::template make<C>(object_ptr, method_ptr);
458 }
459
460 } // namespace tlx
461
462 #endif // !TLX_DELEGATE_HEADER
463
464 /******************************************************************************/
465