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
25namespace tlx {
26
27template <typename T, typename Allocator = std::allocator<void> >
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
59using MyDelegate = Delegate<int(double)>;
60
61// this is a plain function bound to the Delegate as a function pointer
62int func(double a) { return a + 10; }
63MyDelegate d1 = MyDelegate(func);
64
65class AClass {
66public:
67 int method(double d) { return d * d; }
68};
69
70AClass 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>
74MyDelegate d2 = MyDelegate(a, &AClass::method);
75// same as above
76MyDelegate 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!
81MyDelegate 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.
85double offset = 42.0;
86MyDelegate d5 = [&](double a) { return a + offset; };
87\endcode
88 *
89 */
90template <typename R, typename... A, typename Allocator>
91class Delegate<R(A...), Allocator>
92{
93public:
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
306private:
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>
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>
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
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
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
425template <typename T, typename Allocator = std::allocator<void> >
427
428//! constructor for wrapping a class::method with object pointer.
429template <class C, typename R, typename... A>
430inline 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.
437template <class C, typename R, typename... A>
438inline 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.
445template <class C, typename R, typename... A>
446inline 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.
453template <class C, typename R, typename... A>
454inline 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 Delegate &)=default
copy constructor
static R const_method_caller(void *const object_ptr, A &&... args)
function caller for immediate const class::method functions calls.
Definition: delegate.hpp:364
Delegate(Delegate &&)=default
move constructor
static R function_caller(void *const, A &&... args)
caller for an immediate function with no object or pointer.
Definition: delegate.hpp:346
Delegate()=default
default constructor
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 R method_caller(void *const object_ptr, A &&... args)
function caller for immediate class::method function calls
Definition: delegate.hpp:357
Delegate(R(*const function_ptr)(A...)) noexcept
constructor from a plain function pointer with no object.
Definition: delegate.hpp:124
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
static Delegate make(C &object) noexcept
construction for an immediate class::method with class object by reference
Definition: delegate.hpp:157
std::shared_ptr< void > store_
shared_ptr used to contain a memory object containing the callable, like lambdas with closures,...
Definition: delegate.hpp:326
static Delegate make(R(*const function_ptr)(A...)) noexcept
construction from a plain function pointer with no object.
Definition: delegate.hpp:132
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(T &&f)
constructor from any functor object T, which may be a lambda with capture or a MemberPair or ConstMem...
Definition: delegate.hpp:204
Delegate(C *const object_ptr, R(C::*const method_ptr)(A...))
constructor for wrapping a class::method with object pointer.
Definition: delegate.hpp:210
R(*)(void *, A &&...) Caller
type of the function caller pointer.
Definition: delegate.hpp:308
static Delegate make(C *const object_ptr) noexcept
construction for an immediate class::method with class object
Definition: delegate.hpp:143
Delegate(const Caller &m, void *const obj) noexcept
private constructor for plain
Definition: delegate.hpp:329
static void store_deleter(void *const ptr)
deleter for stored functor closures
Definition: delegate.hpp:334
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
static Delegate make() noexcept
construction from an immediate function with no object or pointer.
Definition: delegate.hpp:114
static Delegate make(C &object, R(C::*const method_ptr)(A...))
constructor for wrapping a class::method with object reference.
Definition: delegate.hpp:244
void reset()
reset delegate to invalid.
Definition: delegate.hpp:261
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:384
void swap(Delegate &other) noexcept
swap delegates
Definition: delegate.hpp:266
static Delegate make(C const *const object_ptr) noexcept
construction for an immediate class::method with class object
Definition: delegate.hpp:149
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
std::pair< C *const, R(C::*const)(A...)> MemberPair
wrappers for indirect class::method calls containing (object, method_ptr)
Definition: delegate.hpp:378
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 Delegate make(C const &object) noexcept
construction for an immediate class::method with class object by reference
Definition: delegate.hpp:164
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
static R function_ptr_caller(void *const object_ptr, A &&... args)
caller for a plain function pointer.
Definition: delegate.hpp:351
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...) const)
constructor for wrapping a const class::method with object pointer.
Definition: delegate.hpp:215
static bool operator<(const std::string &a, const StringView &b) noexcept
Less operator to compare a std::string with a StringView lexicographically.
static bool operator==(const std::string &a, const StringView &b) noexcept
Equality operator to compare a std::string with a StringView.
static bool operator!=(const std::string &a, const StringView &b) noexcept
Inequality operator to compare a std::string with a StringView.
STL namespace.
void swap(CountingPtr< A, D > &a1, CountingPtr< A, D > &a2) noexcept
swap enclosed object with another counting pointer (no reference counts need change)
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