Thrill  0.1
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 <[email protected]>
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 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>
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>
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>
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>
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>
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>
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>
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>
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>
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.
261  void reset() { caller_ = nullptr; store_.reset(); }
262 
263  void reset_caller() noexcept { caller_ = nullptr; }
264 
265  //! swap delegates
266  void swap(Delegate& other) noexcept { std::swap(*this, other); }
267 
268  //! compare delegate with another
269  bool operator == (Delegate const& rhs) const noexcept {
270  return (object_ptr_ == rhs.object_ptr_) && (caller_ == rhs.caller_);
271  }
272 
273  //! compare delegate with another
274  bool operator != (Delegate const& rhs) const noexcept {
275  return !operator == (rhs);
276  }
277 
278  //! compare delegate with another
279  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
285  bool operator == (std::nullptr_t const) const noexcept {
286  return caller_ == nullptr;
287  }
288 
289  //! compare delegate with another
290  bool operator != (std::nullptr_t const) const noexcept {
291  return caller_ != nullptr;
292  }
293 
294  //! explicit conversion to bool -> valid or invalid.
295  explicit operator bool () const noexcept { return caller_ != nullptr; }
296 
297  //! most important method: call. The call is forwarded to the selected
298  //! function caller.
299  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
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>
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...)>
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.
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...)>
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>
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<
406  >::type
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<
415  >::type
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> >
427 
428 //! constructor for wrapping a class::method with object pointer.
429 template <class C, typename R, typename... A>
430 inline Delegate<R(A...)>
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...)>
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...)>
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...)>
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 /******************************************************************************/
Delegate(const Caller &m, void *const obj) noexcept
private constructor for plain
Definition: delegate.hpp:329
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:407
static bool operator<(const std::string &a, const StringView &b) noexcept
Less operator to compare a std::string with a StringView lexicographically.
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:237
Delegate(C const &object, R(C::*const method_ptr)(A...) const)
constructor for wrapping a const class::method with object reference.
Definition: delegate.hpp:225
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:230
void reset()
reset delegate to invalid.
Definition: delegate.hpp:261
double T
static R function_ptr_caller(void *const object_ptr, A &&... args)
caller for a plain function pointer.
Definition: delegate.hpp:351
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:431
static void store_deleter(void *const ptr)
deleter for stored functor closures
Definition: delegate.hpp:334
STL namespace.
static R function_caller(void *const, A &&... args)
caller for an immediate function with no object or pointer.
Definition: delegate.hpp:346
std::pair< C const *const, R(C::*const)(A...) const > ConstMemberPair
Definition: delegate.hpp:384
Delegate(R(*const function_ptr)(A...)) noexcept
constructor from a plain function pointer with no object.
Definition: delegate.hpp:124
static bool operator!=(const std::string &a, const StringView &b) noexcept
Inequality operator to compare a std::string with a StringView.
static bool operator==(const std::string &a, const StringView &b) noexcept
Equality operator to compare a std::string with a StringView.
std::shared_ptr< void > store_
Definition: delegate.hpp:326
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:416
R(*)(void *, A &&...) Caller
type of the function caller pointer.
Definition: delegate.hpp:308
void swap(CountingPtr< A, D > &a1, CountingPtr< A, D > &a2) noexcept
static Delegate make(C const *const object_ptr) noexcept
construction for an immediate class::method with class object
Definition: delegate.hpp:149
int value
Definition: gen_data.py:41
static Delegate make(T &&f)
Definition: delegate.hpp:204
Delegate(C &object, R(C::*const method_ptr)(A...))
constructor for wrapping a class::method with object reference.
Definition: delegate.hpp:220
static Delegate make(C const &object) noexcept
Definition: delegate.hpp:164
void swap(Delegate &other) noexcept
swap delegates
Definition: delegate.hpp:266
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:215
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:250
Delegate(C *const object_ptr, R(C::*const method_ptr)(A...))
constructor for wrapping a class::method with object pointer.
Definition: delegate.hpp:210
static Delegate make(C &object) noexcept
Definition: delegate.hpp:157
static R method_caller(void *const object_ptr, A &&... args)
function caller for immediate class::method function calls
Definition: delegate.hpp:357
static Delegate make(C &object, R(C::*const method_ptr)(A...))
constructor for wrapping a class::method with object reference.
Definition: delegate.hpp:244
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
Definition: delegate.hpp:378
static Delegate make() noexcept
construction from an immediate function with no object or pointer.
Definition: delegate.hpp:114
static R const_method_caller(void *const object_ptr, A &&... args)
function caller for immediate const class::method functions calls.
Definition: delegate.hpp:364