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