Mbed OS Reference
Loading...
Searching...
No Matches
Callback.h
1/* mbed Microcontroller Library
2 * Copyright (c) 2006-2019 ARM Limited
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17#ifndef MBED_CALLBACK_H
18#define MBED_CALLBACK_H
19
20#include <cstring>
21#include <mstd_cstddef>
22#include <stdint.h>
23#include <mstd_new>
24#include "platform/mbed_assert.h"
25#include "platform/mbed_toolchain.h"
26#include <mstd_type_traits>
27#include <mstd_functional>
28
29// Controlling switches from config:
30// MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL - support storing non-trivial function objects
31// MBED_CONF_PLATFORM_CALLBACK_COMPARABLE - support memcmp comparing stored objects (requires zero padding)
32
33#ifdef __ICCARM__
34/* Force platform.callback-nontrivial for IAR */
35#undef MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
36#define MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL 1
37#endif
38
39
40namespace mbed {
41/** \addtogroup platform-public-api */
42/** @{*/
43/**
44 * \defgroup platform_Callback Callback class
45 * @{
46 */
47
48/** Callback class based on template specialization
49 *
50 * @note Synchronization level: Not protected
51 */
52template <typename Signature>
54
55namespace detail {
56
57/* Convert pointer-to-member type to member type */
58template <typename T>
59struct member_type { };
60
61template <typename M, class C>
62struct member_type<M C::*> : mstd::type_identity<M> { };
63
64template <typename T>
65using member_type_t = typename member_type<T>::type;
66
67/* Remove cv-qualifiers and lvalue ref-qualifiers */
68/* Not rvalue - we store the function object, so are always going to call it on an lvalue */
69template <typename T>
70struct unqualify_fn { };
71
72// *INDENT-OFF*
73template <typename R, typename... Args>
74struct unqualify_fn<R(Args...)> : mstd::type_identity<R(Args...)> { };
75template <typename R, typename... Args>
76struct unqualify_fn<R(Args...) &> : mstd::type_identity<R(Args...)> { };
77template <typename R, typename... Args>
78struct unqualify_fn<R(Args...) const> : mstd::type_identity<R(Args...)> { };
79template <typename R, typename... Args>
80struct unqualify_fn<R(Args...) const &> : mstd::type_identity<R(Args...)> { };
81template <typename R, typename... Args>
82struct unqualify_fn<R(Args...) volatile> : mstd::type_identity<R(Args...)> { };
83template <typename R, typename... Args>
84struct unqualify_fn<R(Args...) volatile &> : mstd::type_identity<R(Args...)> { };
85template <typename R, typename... Args>
86struct unqualify_fn<R(Args...) const volatile> : mstd::type_identity<R(Args...)> { };
87template <typename R, typename... Args>
88struct unqualify_fn<R(Args...) const volatile &> : mstd::type_identity<R(Args...)> { };
89#if __cplusplus >=201703 || __cpp_noexcept_function_type >= 201510
90/* We have to spell out all c/v/ref/noexcept versions here, as specialization needs exact type match */
91/* Compare to callback() and deduction guides, where dropping the noexcept is a permitted conversion */
92template <typename R, typename... Args>
93struct unqualify_fn<R(Args...) noexcept> : mstd::type_identity<R(Args...)> { };
94template <typename R, typename... Args>
95struct unqualify_fn<R(Args...) & noexcept> : mstd::type_identity<R(Args...)> { };
96template <typename R, typename... Args>
97struct unqualify_fn<R(Args...) const noexcept> : mstd::type_identity<R(Args...)> { };
98template <typename R, typename... Args>
99struct unqualify_fn<R(Args...) const & noexcept> : mstd::type_identity<R(Args...)> { };
100template <typename R, typename... Args>
101struct unqualify_fn<R(Args...) volatile noexcept> : mstd::type_identity<R(Args...)> { };
102template <typename R, typename... Args>
103struct unqualify_fn<R(Args...) volatile & noexcept> : mstd::type_identity<R(Args...)> { };
104template <typename R, typename... Args>
105struct unqualify_fn<R(Args...) const volatile noexcept> : mstd::type_identity<R(Args...)> { };
106template <typename R, typename... Args>
107struct unqualify_fn<R(Args...) const volatile & noexcept> : mstd::type_identity<R(Args...)> { };
108#endif
109
110template <typename T>
111using unqualify_fn_t = typename unqualify_fn<T>::type;
112
113template <typename R, typename F, typename... Args, typename std::enable_if_t<!std::is_void<R>::value, int> = 0>
114R invoke_r(F&& f, Args&&... args)
115{
116 return mstd::invoke(std::forward<F>(f), std::forward<Args>(args)...);
117}
118
119template <typename R, typename F, typename... Args, typename std::enable_if_t<std::is_void<R>::value, int> = 0>
120R invoke_r(F&& f, Args&&... args)
121{
122 mstd::invoke(std::forward<F>(f), std::forward<Args>(args)...);
123}
124
125template<typename F>
127 mstd::disjunction<
128 std::is_function<std::remove_pointer_t<F>>,
129 std::is_member_pointer<F>
130 > {
131};
132// *INDENT-ON*
133
134struct [[gnu::may_alias]] CallbackBase {
135 // Storage is sufficient to hold at least a pointer to member
136 // function, and an object pointer.
138 struct _class;
139 void (_class::*_methodfunc)(int);
140 void *obj;
141 };
142
143 /* Notes on the [[gnu::may_alias]] attribute here.
144 *
145 * The CallbackBase::Store is subject to aliasing problems if ever copied via a trivial copy.
146 * This issue is described here:
147 *
148 * https://answers.launchpad.net/gcc-arm-embedded/+question/686870/+index
149 * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0593r5.html
150 *
151 * The paper p0593 proposes to solve the problem via a language Defect Report, which would make this
152 * code valid - it would become legal to copy a trivial object into char array storage. (But not
153 * aligned_storage_t, as of version 5 - I've suggested a revision).
154 *
155 * Real-life problems have only been seen in GCC when the code used aligned_storage_t.
156 *
157 * The libstdc++ implementation of std::function uses the [[gnu::may_alias]] attribute itself to avoid
158 * problems when it swaps locally-stored functors; we need it for copy-assignment too.
159 *
160 * It appears [[gnu::may_alias]] doesn't work through composition - it's not sufficent to mark just the
161 * `Store` type, we have to mark the whole `Callback` if we're going to let the compiler
162 * implicitly define the trivial copy for it. This potentially could lead to an issue if a `Callback`
163 * was used in a trivially-copyable type itself, but this seems an unlikely use case. The p0593r5
164 * change would, if correctly implemented, work in composition.
165 *
166 * Although, to further increase the confusion, it appears that using a character array does work
167 * fine without may_alias, while aligned_storage_t does not. I've seen a suggestion that GCC 8
168 * may have implicit "may_alias" on character arrays, rendering the attribute in std::function
169 * and here redundant on current GCC:
170 *
171 * https://gcc.gnu.org/ml/gcc-help/2017-06/msg00102.html
172 *
173 * For maximum safety, this version now avoids aligned_storage_t, and also has the possibly-redundant
174 * attribute at each level.
175 *
176 * C++17 says that implementations ignore unrecognized attributes, and IAR+clang comply with this
177 * even in C++14 mode, so we add [[gnu::may_alias]] unconditionally.
178 */
179 struct alignas(_model_function_object) [[gnu::may_alias]] Store {
180 char data[sizeof(_model_function_object)];
181 };
182 Store _storage;
183
184#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
185 // Dynamically dispatched operations
186 const struct ops {
187 void (*call)(); // type-erased function pointer
188 void (*copy)(Store &, const Store &);
189 void (*dtor)(Store &);
190 } *_ops;
191
192 // Control
193 using Control = const ops *;
194
195 // Construct as empty
196 CallbackBase(std::nullptr_t) noexcept : _ops(nullptr) { }
197#else
198 void (*_call)(); // type-erased function pointer
199
200 using Control = void(*)();
201
202 // Construct as empty
203 CallbackBase(std::nullptr_t) noexcept : _call(nullptr) { }
204#endif
205
206 // Default constructor - no initialization
207 CallbackBase() = default;
208
209 Control &control()
210 {
211#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
212 return _ops;
213#else
214 return _call;
215#endif
216 }
217
218 const Control &control() const
219 {
220#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
221 return _ops;
222#else
223 return _call;
224#endif
225 }
226
227 auto call_fn() const
228 {
229#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
230 return _ops->call;
231#else
232 return _call;
233#endif
234 }
235
236 // Clear to empty - does not destroy
237 void clear() noexcept
238 {
239 // For copy efficiency we only zero out the operation pointer
240 // Therefore storage is undefined when we are empty.
241 // Callback-to-Callback comparison operator has to deal with this,
242 // but such comparisons are rare. Comparisons to empty are efficient.
243 control() = nullptr;
244 }
245
246#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
247 // Copy from another CallbackBase - assumes we are uninitialised
248 void copy(const CallbackBase &other)
249 {
250 _ops = other._ops;
251 if (_ops) {
252 _ops->copy(_storage, other._storage);
253 }
254 }
255#else
256 void swap(CallbackBase &other) noexcept
257 {
258 std::swap(_storage, other._storage);
259 std::swap(_call, other._call);
260 }
261#endif
262
263 // Destroy anything we hold - does not reset, so we are in undefined state afterwards.
264 // Must be followed by copy, move, reset, or destruction of the CallbackBase
265 void destroy()
266 {
267#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
268 if (_ops) {
269 _ops->dtor(_storage);
270 }
271#endif
272 }
273
274#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
275 // Copy construct F into storage
276 template <typename F>
277 static void target_copy(Store &d, const Store &p)
278 {
279 const F &f = reinterpret_cast<const F &>(p);
280 new (&d) F(f);
281 }
282
283 // Destroy F in storage
284 template <typename F>
285 static void target_dtor(Store &p)
286 {
287 F &f = reinterpret_cast<F &>(p);
288 f.~F();
289 }
290
291 // Trivial copy construction into storage
292 static void trivial_target_copy(Store &d, const Store &p) noexcept
293 {
294 std::memcpy(&d, &p, sizeof d);
295 }
296
297 // Trivial destruction in storage
298 static void trivial_target_dtor(Store &p) noexcept
299 {
300 }
301#endif
302};
303
304}
305
306/** Callback class based on template specialization
307 *
308 * @note Synchronization level: Not protected
309 */
310template <typename R, typename... ArgTs>
311class Callback<R(ArgTs...)> : private detail::CallbackBase {
312public:
313 using result_type = R;
314
315 /** Create an empty Callback
316 */
317 Callback() noexcept : CallbackBase(nullptr) { }
318
319 /** Create an empty Callback
320 */
321 Callback(std::nullptr_t) noexcept : Callback() { }
322
323#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
324 /** Copy a Callback
325 * @param other The Callback to copy
326 */
327 Callback(const Callback &other) : CallbackBase()
328 {
329 copy(other);
330 }
331
332 /** Move a Callback
333 * @param other The Callback to move
334 */
335 Callback(Callback &&other) : CallbackBase()
336 {
337 // Move constructor exists to ensure that it gets selected
338 // in preference to the universal constructor form.
339 copy(other);
340 }
341#else // MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
342 Callback(const Callback &other) = default;
343 Callback(Callback &&other) = default;
344#endif // MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
345
346 /** Create a Callback with a member function
347 * @param obj Pointer to object to invoke member function on
348 * @param method Member function to attach
349 */
350 template<typename Obj, typename Method, typename std::enable_if_t<mstd::is_invocable_r<R, Method, Obj, ArgTs...>::value, int> = 0>
351 Callback(Obj obj, Method method) : CallbackBase()
352 {
353 generate([obj, method](ArgTs... args) {
354 return detail::invoke_r<R>(method, obj, std::forward<ArgTs>(args)...);
355 });
356 }
357
358 /** Create a Callback with a static function and bound pointer
359 * @param func Static function to attach
360 * @param arg Pointer argument to function
361 */
362 template<typename Fn, typename BoundArg, typename std::enable_if_t<mstd::is_invocable_r<R, Fn, BoundArg, ArgTs...>::value, int> = 0>
363 Callback(Fn func, BoundArg arg) : CallbackBase()
364 {
365 generate([func, arg](ArgTs... args) {
366 return detail::invoke_r<R>(func, arg, std::forward<ArgTs>(args)...);
367 });
368 }
369
370 // *INDENT-OFF*
371 /** Create a Callback with a function object
372 * @param f Function object to attach
373 * @note The function object is limited to a a few words of storage
374 */
375 template <typename F,
376 typename std::enable_if_t<
378 mstd::is_invocable_r<R, F, ArgTs...>::value, int> = 0>
379 Callback(F f) : CallbackBase()
380 {
381 static_assert(std::is_copy_constructible<F>::value, "Callback F must be CopyConstructible");
382 generate(std::move(f));
383 }
384
385 /** Create a Callback with a function pointer
386 * @param f Function pointer to attach
387 */
388 template <typename F,
389 typename std::enable_if_t<
391 mstd::is_invocable_r<R, F, ArgTs...>::value, int> = 0>
392 Callback(F f) : CallbackBase()
393 {
394 static_assert(std::is_copy_constructible<F>::value, "Callback F must be CopyConstructible");
395 if (!f) {
396 clear();
397 } else {
398 generate(std::move(f));
399 }
400 }
401 // *INDENT-ON*
402
403 /** Destroy a callback
404 */
405#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
406 ~Callback()
407 {
408 destroy();
409 }
410#else
411 ~Callback() = default;
412#endif
413
414 /** Swap a callback
415 */
416 void swap(Callback &that) noexcept
417 {
418#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
419 if (this != &that) {
420 Callback temp(std::move(*this));
421 *this = std::move(that);
422 that = std::move(temp);
423 }
424#else
425 CallbackBase::swap(that);
426#endif
427 }
428
429#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
430 /** Assign a callback
431 */
432 Callback &operator=(const Callback &that)
433 {
434 // C++ standard says to use swap, but that's overkill with no exceptions
435 // Callback(f).swap(*this);
436 if (this != &that) {
437 destroy();
438 copy(that);
439 }
440
441 return *this;
442 }
443
444 /** Assign a callback
445 */
446 Callback &operator=(Callback &&that)
447 {
448 if (this != &that) {
449 destroy();
450 copy(that);
451 }
452
453 return *this;
454 }
455#else // MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
456 Callback &operator=(const Callback &that) = default;
457 Callback &operator=(Callback &&that) = default;
458#endif // MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
459
460 /** Assign a callback
461 */
462 // C++ std::function lacks the is_same restriction here, which would mean non-const lvalue references hit this,
463 // rather than the normal copy assignment (`F &&` is a better match for `Callback &` than `const Callback &`).
464 // Wouldn't matter if both used the swap form, but having cut it down, for code size want to ensure we don't use this
465 // instead of copy assignment. (If nontrivial disabled, definitely want to use the default copy assignment, and
466 // if nontrivial enabled, we know this doesn't handle self-assignment).
467 // *INDENT-OFF*
468 template <typename F,
469 typename = std::enable_if_t<
470 mstd::is_invocable_r<R, F, ArgTs...>::value &&
471 !mstd::is_same<mstd::remove_cvref_t<F>, Callback>::value>>
473 {
474 // C++ standard says to use swap, but that's overkill with no exceptions
475 // Callback(std::forward<F>(that)).swap(*this);
476 this->~Callback();
477 new (this) Callback(std::forward<F>(f));
478 return *this;
479 }
480 // *INDENT-ON*
481
482 template <typename F>
483 Callback &operator=(std::reference_wrapper<F> f) noexcept
484 {
485 // C++ standard says to use swap, but that's overkill with no exceptions
486 // Callback(f).swap(*this);
487 this->~Callback();
488 new (this) Callback(f);
489 return *this;
490 }
491
492 /** Empty a callback
493 */
494 Callback &operator=(std::nullptr_t) noexcept
495 {
496 destroy();
497 clear();
498
499 return *this;
500 }
501
502 /** Call the attached function
503 */
504 R call(ArgTs... args) const
505 {
506 MBED_ASSERT(bool(*this));
507 auto op_call = reinterpret_cast<call_type *>(call_fn());
508 return op_call(this, args...);
509 }
510
511 /** Call the attached function
512 */
513 R operator()(ArgTs... args) const
514 {
515 return call(args...);
516 }
517
518 /** Test if function has been assigned
519 */
520 explicit operator bool() const noexcept
521 {
522 return control();
523 }
524
525#if MBED_CONF_PLATFORM_CALLBACK_COMPARABLE
526 /** Test for equality
527 *
528 * @note This only compares stored objects byte-wise using memcmp
529 * after checking that they're the same type. It does *not* use
530 * any equality operator defined for the class.
531 *
532 * @note This is an extension compared to std::function, and supporting
533 * it requires extra code even if the comparison is never used.
534 * Avoid using this operator if possible, so that the option
535 * `platform.callback-comparable` can be turned off to save ROM.
536 */
537 friend bool operator==(const Callback &l, const Callback &r) noexcept
538 {
539 if (l.control() != r.control()) {
540 /* Type of stored object differs */
541 return false;
542 }
543 if (!l) {
544 /* Both must be empty, as we checked the types match. Do not
545 * check storage in this case - this simplifies clear(), and
546 * clears are far more common than callback comparison.
547 */
548 return true;
549 }
550 return memcmp(&l._storage, &r._storage, sizeof(Store)) == 0;
551 }
552#endif
553
554 /** Test for emptiness
555 */
556 friend bool operator==(const Callback &f, std::nullptr_t) noexcept
557 {
558 return !f;
559 }
560
561 /** Test for emptiness
562 */
563 friend bool operator==(std::nullptr_t, const Callback &f) noexcept
564 {
565 return !f;
566 }
567
568#if MBED_CONF_PLATFORM_CALLBACK_COMPARABLE
569 /** Test for inequality
570 *
571 * @see operator==(const Callback &l, const Callback &r)
572 */
573 friend bool operator!=(const Callback &l, const Callback &r) noexcept
574 {
575 return !(l == r);
576 }
577#endif
578
579 /** Test for non-emptiness
580 */
581 friend bool operator!=(const Callback &f, std::nullptr_t) noexcept
582 {
583 return bool(f);
584 }
585
586 /** Test for non-emptiness
587 */
588 friend bool operator!=(std::nullptr_t, const Callback &f) noexcept
589 {
590 return bool(f);
591 }
592
593 /** Static thunk for passing as C-style function
594 * @param func Callback to call passed as void pointer
595 * @param args Arguments to be called with function func
596 * @return the value as determined by func which is of
597 * type and determined by the signature of func
598 */
599 static R thunk(void *func, ArgTs... args)
600 {
601 return static_cast<Callback *>(func)->call(args...);
602 }
603
604private:
605 using call_type = R(const CallbackBase *, ArgTs...);
606
607 // *INDENT-OFF*
608 // Generate operations for function object
609 // Storage assumed to be uninitialised - destructor should have already been called if it was previously used
610 // When generating, function object should always be moved
611 template <typename F, typename = std::enable_if_t<!std::is_lvalue_reference<F>::value>>
612 void generate(F &&f)
613 {
614#ifndef __ICCARM__ /* This assert fails on IAR for unknown reason */
615 static_assert(std::is_same<decltype(target_call<F>), call_type>::value, "Call type mismatch");
616#endif
617 static_assert(sizeof(Callback) == sizeof(CallbackBase), "Callback should be same size as CallbackBase");
618 static_assert(std::is_trivially_copyable<CallbackBase>::value, "CallbackBase expected to be TriviallyCopyable");
619
620 // Set the control pointer
621#if MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
622 // Generates one static ops for each <F,R,ArgTs...> tuple
623 // But the functions used for copy/move/dtor depend only on F, and even then only if non-trivial.
624 // `call` is type-erased - we cast from our call_type to the void (*)(void) in CallbackBase
625 // This should be a ROMmed constant table, but formally it can't be constexpr because of the reinterpret_cast :(
626 static const ops ops = {
627 reinterpret_cast<void (*)()>(target_call<F>),
628 std::is_trivially_copy_constructible<F>::value ? trivial_target_copy : target_copy<F>,
629 std::is_trivially_destructible<F>::value ? trivial_target_dtor : target_dtor<F>,
630 };
631 _ops = &ops;
632#else // MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
633 // Avoid the need for the const ops table - just one function pointer in the Callback itself
634 _call = reinterpret_cast<void (*)()>(target_call<F>);
635 static_assert(std::is_trivially_copyable<F>::value, "F must be TriviallyCopyable. Turn on Mbed configuration option 'platform.callback-nontrivial' to use more complex function objects");
636 static_assert(std::is_trivially_copyable<Callback>::value, "Callback expected to be TriviallyCopyable");
637#endif // MBED_CONF_PLATFORM_CALLBACK_NONTRIVIAL
638
639 // Move the functor into storage
640 static_assert(sizeof(F) <= sizeof(Store) && alignof(F) <= alignof(Store),
641 "Type F must not exceed the size of the Callback class");
642 new (&_storage) F(std::move(f));
643
644#if MBED_CONF_PLATFORM_CALLBACK_COMPARABLE
645 // Zero out any padding - required for Callback-to-Callback comparisons.
646 if (sizeof(F) < sizeof(Store)) {
647 std::memset(reinterpret_cast<char *>(&_storage) + sizeof(F), 0, sizeof(Store) - sizeof(F));
648 }
649#endif
650 }
651 // *INDENT-ON*
652
653 // Target call routine - custom needed for each <F,R,ArgTs...> tuple
654 template <typename F>
655 static R target_call(const CallbackBase *p, ArgTs... args)
656 {
657 // Need for const_cast here correlates to a std::function bug - see P0045 and N4159
658 F &f = const_cast<F &>(reinterpret_cast<const F &>(p->_storage));
659 return detail::invoke_r<R>(f, std::forward<ArgTs>(args)...);
660 }
661};
662
663// Internally used event type
664using event_callback_t = Callback<void(int)>;
665
666template <typename R, typename... ArgTs>
667void swap(Callback<R(ArgTs...)> &lhs, Callback<R(ArgTs...)> &rhs) noexcept
668{
669 lhs.swap(rhs);
670}
671
672/** Create a callback class with type inferred from the arguments
673 *
674 * @param func Static function to attach
675 * @return Callback with inferred type
676 */
677template <typename R, typename... ArgTs>
678Callback<R(ArgTs...)> callback(R(*func)(ArgTs...) = nullptr) noexcept
679{
680 return Callback<R(ArgTs...)>(func);
681}
682
683/** Create a callback class with type inferred from the arguments
684 *
685 * @param func Static function to attach
686 * @return Callback with inferred type
687 */
688template <typename R, typename... ArgTs>
689Callback<R(ArgTs...)> callback(const Callback<R(ArgTs...)> &func)
690{
691 return Callback<R(ArgTs...)>(func);
692}
693
694/** Create a callback class with type inferred from the arguments
695 *
696 * @param func Static function to attach
697 * @return Callback with inferred type
698 */
699template <typename R, typename... ArgTs>
700Callback<R(ArgTs...)> callback(Callback<R(ArgTs...)> &&func) noexcept
701{
702 return Callback<R(ArgTs...)>(std::move(func));
703}
704
705/** Create a callback class with type inferred from the arguments
706 *
707 * @param obj Optional pointer to object to bind to function
708 * @param method Member function to attach
709 * @return Callback with inferred type
710 */
711template<typename T, typename U, typename R, typename... ArgTs>
712Callback<R(ArgTs...)> callback(U *obj, R(T::*method)(ArgTs...)) noexcept
713{
714 return Callback<R(ArgTs...)>(obj, method);
715}
716
717template<typename T, typename U, typename R, typename... ArgTs>
718Callback<R(ArgTs...)> callback(U *obj, R(T::*method)(ArgTs...) &) noexcept
719{
720 return Callback<R(ArgTs...)>(obj, method);
721}
722
723template<typename T, typename U, typename R, typename... ArgTs>
724Callback<R(ArgTs...)> callback(const U *obj, R(T::*method)(ArgTs...) const) noexcept
725{
726 return Callback<R(ArgTs...)>(obj, method);
727}
728
729template<typename T, typename U, typename R, typename... ArgTs>
730Callback<R(ArgTs...)> callback(const U *obj, R(T::*method)(ArgTs...) const &) noexcept
731{
732 return Callback<R(ArgTs...)>(obj, method);
733}
734
735template<typename T, typename U, typename R, typename... ArgTs>
736Callback<R(ArgTs...)> callback(volatile U *obj, R(T::*method)(ArgTs...) volatile) noexcept
737{
738 return Callback<R(ArgTs...)>(obj, method);
739}
740
741template<typename T, typename U, typename R, typename... ArgTs>
742Callback<R(ArgTs...)> callback(volatile U *obj, R(T::*method)(ArgTs...) volatile &) noexcept
743{
744 return Callback<R(ArgTs...)>(obj, method);
745}
746
747template<typename T, typename U, typename R, typename... ArgTs>
748Callback<R(ArgTs...)> callback(const volatile U *obj, R(T::*method)(ArgTs...) const volatile) noexcept
749{
750 return Callback<R(ArgTs...)>(obj, method);
751}
752
753template<typename T, typename U, typename R, typename... ArgTs>
754Callback<R(ArgTs...)> callback(const volatile U *obj, R(T::*method)(ArgTs...) const volatile &) noexcept
755{
756 return Callback<R(ArgTs...)>(obj, method);
757}
758
759/** Create a callback class with type inferred from the arguments
760 *
761 * @param func Static function to attach
762 * @param arg Pointer argument to function
763 * @return Callback with inferred type
764 */
765template <typename T, typename U, typename R, typename... ArgTs>
766Callback<R(ArgTs...)> callback(R(*func)(T *, ArgTs...), U *arg) noexcept
767{
768 return Callback<R(ArgTs...)>(func, arg);
769}
770
771template <typename T, typename U, typename R, typename... ArgTs>
772Callback<R(ArgTs...)> callback(R(*func)(const T *, ArgTs...), const U *arg) noexcept
773{
774 return Callback<R(ArgTs...)>(func, arg);
775}
776
777template <typename T, typename U, typename R, typename... ArgTs>
778Callback<R(ArgTs...)> callback(R(*func)(volatile T *, ArgTs...), volatile U *arg) noexcept
779{
780 return Callback<R(ArgTs...)>(func, arg);
781}
782
783template <typename T, typename U, typename R, typename... ArgTs>
784Callback<R(ArgTs...)> callback(R(*func)(const volatile T *, ArgTs...), const volatile U *arg) noexcept
785{
786 return Callback<R(ArgTs...)>(func, arg);
787}
788
789/** Create a Create a callback class with type inferred from the arguments
790 * @param f Function object to attach
791 * @note The function object is limited to a single word of storage
792 */
793template <typename F>
794Callback<detail::unqualify_fn_t<detail::member_type_t<decltype(&mstd::remove_cvref_t<F>::operator())>>>
796{
797 return Callback<detail::unqualify_fn_t<detail::member_type_t<decltype(&mstd::remove_cvref_t<F>::operator())>>>(std::forward<F>(f));
798}
799
800#if __cplusplus >= 201703 || __cpp_deduction_guides >= 201703
801/* Deduction guides that can replace callback() helper */
802template <typename R, typename... Args>
803Callback(R(*)(Args...)) -> Callback<R(Args...)>;
804template <typename F>
805Callback(F) -> Callback<detail::unqualify_fn_t<detail::member_type_t<decltype(&F::operator())>>>;
806template <typename T, typename U, typename R, typename... ArgTs>
807Callback(U *obj, R(T::*method)(ArgTs...)) -> Callback<R(ArgTs...)>;
808template <typename T, typename U, typename R, typename... ArgTs>
809Callback(U *obj, R(T::*method)(ArgTs...) &) -> Callback<R(ArgTs...)>;
810template <typename T, typename U, typename R, typename... ArgTs>
811Callback(const U *obj, R(T::*method)(ArgTs...) const) -> Callback<R(ArgTs...)>;
812template <typename T, typename U, typename R, typename... ArgTs>
813Callback(const U *obj, R(T::*method)(ArgTs...) const &) -> Callback<R(ArgTs...)>;
814template <typename T, typename U, typename R, typename... ArgTs>
815Callback(volatile U *obj, R(T::*method)(ArgTs...) volatile) -> Callback<R(ArgTs...)>;
816template <typename T, typename U, typename R, typename... ArgTs>
817Callback(volatile U *obj, R(T::*method)(ArgTs...) volatile &) -> Callback<R(ArgTs...)>;
818template <typename T, typename U, typename R, typename... ArgTs>
819Callback(const volatile U *obj, R(T::*method)(ArgTs...) const volatile) -> Callback<R(ArgTs...)>;
820template <typename T, typename U, typename R, typename... ArgTs>
821Callback(const volatile U *obj, R(T::*method)(ArgTs...) const volatile &) -> Callback<R(ArgTs...)>;
822template <typename T, typename U, typename R, typename... ArgTs>
823Callback(R(*func)(T *, ArgTs...), U *arg) -> Callback<R(ArgTs...)>;
824template <typename T, typename U, typename R, typename... ArgTs>
825Callback(R(*func)(const T *, ArgTs...), const U *arg) -> Callback<R(ArgTs...)>;
826template <typename T, typename U, typename R, typename... ArgTs>
827Callback(R(*func)(volatile T *, ArgTs...), volatile U *arg) -> Callback<R(ArgTs...)>;
828template <typename T, typename U, typename R, typename... ArgTs>
829Callback(R(*func)(const volatile T *, ArgTs...), const volatile U *arg) -> Callback<R(ArgTs...)>;
830#endif
831
832/**@}*/
833
834/**@}*/
835
836} // namespace mbed
837
838#endif
Callback(Fn func, BoundArg arg)
Create a Callback with a static function and bound pointer.
Definition: Callback.h:363
void swap(Callback &that) noexcept
Swap a callback.
Definition: Callback.h:416
Callback(std::nullptr_t) noexcept
Create an empty Callback.
Definition: Callback.h:321
friend bool operator==(const Callback &f, std::nullptr_t) noexcept
Test for emptiness.
Definition: Callback.h:556
R call(ArgTs... args) const
Call the attached function.
Definition: Callback.h:504
Callback & operator=(F &&f)
Assign a callback.
Definition: Callback.h:472
~Callback()=default
Destroy a callback.
Callback & operator=(std::nullptr_t) noexcept
Empty a callback.
Definition: Callback.h:494
friend bool operator!=(std::nullptr_t, const Callback &f) noexcept
Test for non-emptiness.
Definition: Callback.h:588
Callback() noexcept
Create an empty Callback.
Definition: Callback.h:317
friend bool operator==(std::nullptr_t, const Callback &f) noexcept
Test for emptiness.
Definition: Callback.h:563
Callback(Obj obj, Method method)
Create a Callback with a member function.
Definition: Callback.h:351
R operator()(ArgTs... args) const
Call the attached function.
Definition: Callback.h:513
Callback(F f)
Create a Callback with a function object.
Definition: Callback.h:379
friend bool operator!=(const Callback &f, std::nullptr_t) noexcept
Test for non-emptiness.
Definition: Callback.h:581
static R thunk(void *func, ArgTs... args)
Static thunk for passing as C-style function.
Definition: Callback.h:599
Callback class based on template specialization.
Definition: Callback.h:53
void operator!=(const SafeBool< T > &lhs, const SafeBool< U > &rhs)
Avoid conversion to bool between different classes.
void operator==(const SafeBool< T > &lhs, const SafeBool< U > &rhs)
Avoid conversion to bool between different classes.
#define MBED_ASSERT(expr)
MBED_ASSERT Declare runtime assertions: results in runtime error if condition is false.
Definition: mbed_assert.h:66
Callback< R(ArgTs...)> callback(R(*func)(ArgTs...)=nullptr) noexcept
Create a callback class with type inferred from the arguments.
Definition: Callback.h:678