tlx
delegate.hpp
Go to the documentation of this file.
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...)>
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.
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.
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...)>
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>
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...)>
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>
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  >
182  Delegate(T&& f)
183  : store_(
184  // allocate memory for T in shared_ptr with appropriate deleter
185  typename std::allocator_traits<Allocator>::template rebind_alloc<
186  typename std::decay<T>::type>{}.allocate(1),
187  store_deleter<typename std::decay<T>::type>, Allocator()) {
188 
189  using Functor = typename std::decay<T>::type;
190  using Rebind = typename std::allocator_traits<Allocator>::template rebind_alloc<Functor>;
191 
192  // copy-construct T into shared_ptr memory.
193  Rebind rebind{};
194  std::allocator_traits<Rebind>::construct(
195  rebind, static_cast<Functor*>(store_.get()), Functor(std::forward<T>(f)));
196 
197  object_ptr_ = store_.get();
198 
199  caller_ = functor_caller<Functor>;
200  }
201 
202  //! constructor from any functor object T, which may be a lambda with
203  //! capture or a MemberPair or ConstMemberPair wrapper.
204  template <typename T>
205  static Delegate make(T&& f) {
206  return std::forward<T>(f);
207  }
208 
209  //! constructor for wrapping a class::method with object pointer.
210  template <class C>
211  Delegate(C* const object_ptr, R(C::* const method_ptr)(A...))
212  : Delegate(MemberPair<C>(object_ptr, method_ptr)) { }
213 
214  //! constructor for wrapping a const class::method with object pointer.
215  template <class C>
216  Delegate(C* const object_ptr, R(C::* const method_ptr)(A...) const)
217  : Delegate(ConstMemberPair<C>(object_ptr, method_ptr)) { }
218 
219  //! constructor for wrapping a class::method with object reference.
220  template <class C>
221  Delegate(C& object, R(C::* const method_ptr)(A...))
222  : Delegate(MemberPair<C>(&object, method_ptr)) { }
223 
224  //! constructor for wrapping a const class::method with object reference.
225  template <class C>
226  Delegate(C const& object, R(C::* const method_ptr)(A...) const)
227  : Delegate(ConstMemberPair<C>(&object, method_ptr)) { }
228 
229  //! constructor for wrapping a class::method with object pointer.
230  template <class C>
231  static Delegate make(C* const object_ptr,
232  R(C::* const method_ptr)(A...)) {
233  return MemberPair<C>(object_ptr, method_ptr);
234  }
235 
236  //! constructor for wrapping a const class::method with object pointer.
237  template <class C>
238  static Delegate make(C const* const object_ptr,
239  R(C::* const method_ptr)(A...) const) {
240  return ConstMemberPair<C>(object_ptr, method_ptr);
241  }
242 
243  //! constructor for wrapping a class::method with object reference.
244  template <class C>
245  static Delegate make(C& object, R(C::* const method_ptr)(A...)) {
246  return MemberPair<C>(&object, method_ptr);
247  }
248 
249  //! constructor for wrapping a const class::method with object reference.
250  template <class C>
251  static Delegate make(C const& object,
252  R(C::* const method_ptr)(A...) const) {
253  return ConstMemberPair<C>(&object, method_ptr);
254  }
255 
256  //! \}
257 
258  //! \name Miscellaneous
259  //! \{
260 
261  //! reset delegate to invalid.
262  void reset() { caller_ = nullptr; store_.reset(); }
263 
264  void reset_caller() noexcept { caller_ = nullptr; }
265 
266  //! swap delegates
267  void swap(Delegate& other) noexcept { std::swap(*this, other); }
268 
269  //! compare delegate with another
270  bool operator == (Delegate const& rhs) const noexcept {
271  return (object_ptr_ == rhs.object_ptr_) && (caller_ == rhs.caller_);
272  }
273 
274  //! compare delegate with another
275  bool operator != (Delegate const& rhs) const noexcept {
276  return !operator == (rhs);
277  }
278 
279  //! compare delegate with another
280  bool operator < (Delegate const& rhs) const noexcept {
281  return (object_ptr_ < rhs.object_ptr_) ||
282  ((object_ptr_ == rhs.object_ptr_) && (caller_ < rhs.caller_));
283  }
284 
285  //! compare delegate with another
286  bool operator == (std::nullptr_t const) const noexcept {
287  return caller_ == nullptr;
288  }
289 
290  //! compare delegate with another
291  bool operator != (std::nullptr_t const) const noexcept {
292  return caller_ != nullptr;
293  }
294 
295  //! explicit conversion to bool -> valid or invalid.
296  explicit operator bool () const noexcept { return caller_ != nullptr; }
297 
298  //! most important method: call. The call is forwarded to the selected
299  //! function caller.
300  R operator () (A... args) const {
301  assert(caller_);
302  return caller_(object_ptr_, std::forward<A>(args) ...);
303  }
304 
305  //! \}
306 
307 private:
308  //! type of the function caller pointer.
309  using Caller = R (*)(void*, A&& ...);
310 
311  using Deleter = void (*)(void*);
312 
313  //! pointer to function caller which depends on the type in object_ptr_. The
314  //! caller_ contains a plain pointer to either function_caller, a
315  //! function_ptr_caller, a method_caller, a const_method_caller, or a
316  //! functor_caller.
317  Caller caller_ = nullptr;
318 
319  //! pointer to object held by the delegate: for plain function pointers it
320  //! is the function pointer, for class::methods it is a pointer to the class
321  //! instance, for functors it is a pointer to the shared_ptr store_
322  //! contents.
323  void* object_ptr_ = nullptr;
324 
325  //! shared_ptr used to contain a memory object containing the callable, like
326  //! lambdas with closures, or our own wrappers.
327  std::shared_ptr<void> store_;
328 
329  //! private constructor for plain
330  Delegate(const Caller& m, void* const obj) noexcept
331  : caller_(m), object_ptr_(obj) { }
332 
333  //! deleter for stored functor closures
334  template <typename T>
335  static void store_deleter(void* const ptr) {
336  using Rebind = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
337  Rebind rebind{};
338 
339  std::allocator_traits<Rebind>::destroy(rebind, static_cast<T*>(ptr));
340  std::allocator_traits<Rebind>::deallocate(rebind, static_cast<T*>(ptr), 1);
341  }
342 
343  //! \name Callers for simple function and immediate class::method calls.
344  //! \{
345 
346  //! caller for an immediate function with no object or pointer.
347  template <R(* Function)(A...)>
348  static R function_caller(void* const, A&& ... args) {
349  return Function(std::forward<A>(args) ...);
350  }
351 
352  //! caller for a plain function pointer.
353  static R function_ptr_caller(void* const object_ptr, A&& ... args) {
354  return (*reinterpret_cast<R(* const*)(A...)>(&object_ptr))(args...);
355  }
356 
357  //! function caller for immediate class::method function calls
358  template <class C, R(C::* method_ptr)(A...)>
359  static R method_caller(void* const object_ptr, A&& ... args) {
360  return (static_cast<C*>(object_ptr)->*method_ptr)(
361  std::forward<A>(args) ...);
362  }
363 
364  //! function caller for immediate const class::method functions calls.
365  template <class C, R(C::* method_ptr)(A...) const>
366  static R const_method_caller(void* const object_ptr, A&& ... args) {
367  return (static_cast<C const*>(object_ptr)->*method_ptr)(
368  std::forward<A>(args) ...);
369  }
370 
371  //! \}
372 
373  //! \name Wrappers for indirect class::method calls.
374  //! \{
375 
376  //! wrappers for indirect class::method calls containing (object,
377  //! method_ptr)
378  template <class C>
379  using MemberPair =
380  std::pair<C* const, R(C::* const)(A...)>;
381 
382  //! wrappers for indirect const class::method calls containing (object,
383  //! const method_ptr)
384  template <class C>
385  using ConstMemberPair =
386  std::pair<C const* const, R(C::* const)(A...) const>;
387 
388  //! template for class::function selector
389  template <typename>
390  struct IsMemberPair : std::false_type { };
391 
392  //! specialization for class::function selector
393  template <class C>
394  struct IsMemberPair<MemberPair<C> >: std::true_type { };
395 
396  //! template for const class::function selector
397  template <typename>
398  struct IsConstMemberPair : std::false_type { };
399 
400  //! specialization for const class::function selector
401  template <class C>
402  struct IsConstMemberPair<ConstMemberPair<C> >: std::true_type { };
403 
404  //! function caller for functor class.
405  template <typename T>
406  static typename std::enable_if<
407  !(IsMemberPair<T>::value || IsConstMemberPair<T>::value), R
408  >::type
409  functor_caller(void* const object_ptr, A&& ... args) {
410  return (*static_cast<T*>(object_ptr))(std::forward<A>(args) ...);
411  }
412 
413  //! function caller for const functor class.
414  template <typename T>
415  static typename std::enable_if<
416  (IsMemberPair<T>::value || IsConstMemberPair<T>::value), R
417  >::type
418  functor_caller(void* const object_ptr, A&& ... args) {
419  return (static_cast<T*>(object_ptr)->first->*
420  static_cast<T*>(object_ptr)->second)(std::forward<A>(args) ...);
421  }
422 
423  //! \}
424 };
425 
426 //! make template alias due to similarity with std::function
427 template <typename T, typename Allocator = std::allocator<void> >
429 
430 //! constructor for wrapping a class::method with object pointer.
431 template <class C, typename R, typename... A>
432 inline Delegate<R(A...)>
434  C* const object_ptr, R(C::* const method_ptr)(A...)) noexcept {
435  return Delegate<R(A...)>::template make<C>(object_ptr, method_ptr);
436 }
437 
438 //! constructor for wrapping a const class::method with object pointer.
439 template <class C, typename R, typename... A>
440 inline Delegate<R(A...)>
442  C* const object_ptr, R(C::* const method_ptr)(A...) const) noexcept {
443  return Delegate<R(A...)>::template make<C>(object_ptr, method_ptr);
444 }
445 
446 //! constructor for wrapping a class::method with object reference.
447 template <class C, typename R, typename... A>
448 inline Delegate<R(A...)>
450  C& object_ptr, R(C::* const method_ptr)(A...)) noexcept { // NOLINT
451  return Delegate<R(A...)>::template make<C>(object_ptr, method_ptr);
452 }
453 
454 //! constructor for wrapping a const class::method with object reference.
455 template <class C, typename R, typename... A>
456 inline Delegate<R(A...)>
458  C const& object_ptr, R(C::* const method_ptr)(A...) const) noexcept {
459  return Delegate<R(A...)>::template make<C>(object_ptr, method_ptr);
460 }
461 
462 } // namespace tlx
463 
464 #endif // !TLX_DELEGATE_HEADER
465 
466 /******************************************************************************/
Delegate(const Caller &m, void *const obj) noexcept
private constructor for plain
Definition: delegate.hpp:330
static Delegate make(C const *const object_ptr, R(C::*const method_ptr)(A...) const)
constructor for wrapping a const class::method with object pointer.
Definition: delegate.hpp:238
Delegate(C const &object, R(C::*const method_ptr)(A...) const)
constructor for wrapping a const class::method with object reference.
Definition: delegate.hpp:226
static Delegate make(C *const object_ptr) noexcept
construction for an immediate class::method with class object
Definition: delegate.hpp:143
static Delegate make(C *const object_ptr, R(C::*const method_ptr)(A...))
constructor for wrapping a class::method with object pointer.
Definition: delegate.hpp:231
void reset()
reset delegate to invalid.
Definition: delegate.hpp:262
static std::enable_if< !(IsMemberPair< T >::value||IsConstMemberPair< T >::value), R >::type functor_caller(void *const object_ptr, A &&...args)
function caller for functor class.
Definition: delegate.hpp:409
Delegate< R(A...)> make_delegate(C *const object_ptr, R(C::*const method_ptr)(A...)) noexcept
constructor for wrapping a class::method with object pointer.
Definition: delegate.hpp:433
static std::enable_if< (IsMemberPair< T >::value||IsConstMemberPair< T >::value), R >::type functor_caller(void *const object_ptr, A &&...args)
function caller for const functor class.
Definition: delegate.hpp:418
static void store_deleter(void *const ptr)
deleter for stored functor closures
Definition: delegate.hpp:335
static bool operator!=(const StringView &a, const std::string &b) noexcept
inequality operator to compare a StringView with a std::string
STL namespace.
static R const_method_caller(void *const object_ptr, A &&...args)
function caller for immediate const class::method functions calls.
Definition: delegate.hpp:366
std::pair< C const *const, R(C::*const)(A...) const > ConstMemberPair
wrappers for indirect const class::method calls containing (object, const method_ptr) ...
Definition: delegate.hpp:386
Delegate(R(*const function_ptr)(A...)) noexcept
constructor from a plain function pointer with no object.
Definition: delegate.hpp:124
static R method_caller(void *const object_ptr, A &&...args)
function caller for immediate class::method function calls
Definition: delegate.hpp:359
std::shared_ptr< void > store_
shared_ptr used to contain a memory object containing the callable, like lambdas with closures...
Definition: delegate.hpp:327
R(*)(void *, A &&...) Caller
type of the function caller pointer.
Definition: delegate.hpp:309
void swap(CountingPtr< A, D > &a1, CountingPtr< A, D > &a2) noexcept
swap enclosed object with another counting pointer (no reference counts need change) ...
static Delegate make(C const *const object_ptr) noexcept
construction for an immediate class::method with class object
Definition: delegate.hpp:149
static Delegate make(T &&f)
constructor from any functor object T, which may be a lambda with capture or a MemberPair or ConstMem...
Definition: delegate.hpp:205
Delegate(C &object, R(C::*const method_ptr)(A...))
constructor for wrapping a class::method with object reference.
Definition: delegate.hpp:221
static Delegate make(C const &object) noexcept
construction for an immediate class::method with class object by reference
Definition: delegate.hpp:164
void swap(Delegate &other) noexcept
swap delegates
Definition: delegate.hpp:267
static bool operator==(const StringView &a, const std::string &b) noexcept
equality operator to compare a StringView with a std::string
Delegate(C *const object_ptr, R(C::*const method_ptr)(A...) const)
constructor for wrapping a const class::method with object pointer.
Definition: delegate.hpp:216
static R function_ptr_caller(void *const object_ptr, A &&...args)
caller for a plain function pointer.
Definition: delegate.hpp:353
static Delegate make(C const &object, R(C::*const method_ptr)(A...) const)
constructor for wrapping a const class::method with object reference.
Definition: delegate.hpp:251
Delegate(C *const object_ptr, R(C::*const method_ptr)(A...))
constructor for wrapping a class::method with object pointer.
Definition: delegate.hpp:211
Delegate(T &&f)
constructor from any functor object T, which may be a lambda with capture or a MemberPair or ConstMem...
Definition: delegate.hpp:182
static Delegate make(C &object) noexcept
construction for an immediate class::method with class object by reference
Definition: delegate.hpp:157
static bool operator<(const StringView &a, const std::string &b) noexcept
less operator to compare a StringView with a std::string lexicographically
static Delegate make(C &object, R(C::*const method_ptr)(A...))
constructor for wrapping a class::method with object reference.
Definition: delegate.hpp:245
static Delegate make(R(*const function_ptr)(A...)) noexcept
construction from a plain function pointer with no object.
Definition: delegate.hpp:132
std::pair< C *const, R(C::*const)(A...)> MemberPair
wrappers for indirect class::method calls containing (object, method_ptr)
Definition: delegate.hpp:380
static R function_caller(void *const, A &&...args)
caller for an immediate function with no object or pointer.
Definition: delegate.hpp:348
static Delegate make() noexcept
construction from an immediate function with no object or pointer.
Definition: delegate.hpp:114