tlx
Loading...
Searching...
No Matches
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 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_) && (reinterpret_cast<const void*>(caller_) < reinterpret_cast<const void*>(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
307private:
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>
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>
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
427template <typename T, typename Allocator = std::allocator<void> >
429
430//! constructor for wrapping a class::method with object pointer.
431template <class C, typename R, typename... A>
432inline 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.
439template <class C, typename R, typename... A>
440inline 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.
447template <class C, typename R, typename... A>
448inline 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.
455template <class C, typename R, typename... A>
456inline 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 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:366
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:348
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:359
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:418
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:327
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:221
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 *const object_ptr, R(C::*const method_ptr)(A...))
constructor for wrapping a class::method with object pointer.
Definition: delegate.hpp:211
R(*)(void *, A &&...) Caller
type of the function caller pointer.
Definition: delegate.hpp:309
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:330
static void store_deleter(void *const ptr)
deleter for stored functor closures
Definition: delegate.hpp:335
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
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:245
void reset()
reset delegate to invalid.
Definition: delegate.hpp:262
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
void swap(Delegate &other) noexcept
swap delegates
Definition: delegate.hpp:267
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:226
std::pair< C *const, R(C::*const)(A...)> MemberPair
wrappers for indirect class::method calls containing (object, method_ptr)
Definition: delegate.hpp:380
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
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:238
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...) const)
constructor for wrapping a const class::method with object pointer.
Definition: delegate.hpp:216
static bool operator==(const StringView &a, const std::string &b) noexcept
equality operator to compare a StringView with a std::string
static bool operator<(const StringView &a, const std::string &b) noexcept
less operator to compare a StringView with a std::string lexicographically
static bool operator!=(const StringView &a, const std::string &b) noexcept
inequality operator to compare a StringView with a std::string
STL namespace.
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