Mistake on this page?
Report an issue in GitHub or email us
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 
40 namespace 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  */
52 template <typename Signature>
53 class Callback;
54 
55 namespace detail {
56 
57 /* Convert pointer-to-member type to member type */
58 template <typename T>
59 struct member_type { };
60 
61 template <typename M, class C>
62 struct member_type<M C::*> : mstd::type_identity<M> { };
63 
64 template <typename T>
65 using 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 */
69 template <typename T>
70 struct unqualify_fn { };
71 
72 // *INDENT-OFF*
73 template <typename R, typename... Args>
74 struct unqualify_fn<R(Args...)> : mstd::type_identity<R(Args...)> { };
75 template <typename R, typename... Args>
76 struct unqualify_fn<R(Args...) &> : mstd::type_identity<R(Args...)> { };
77 template <typename R, typename... Args>
78 struct unqualify_fn<R(Args...) const> : mstd::type_identity<R(Args...)> { };
79 template <typename R, typename... Args>
80 struct unqualify_fn<R(Args...) const &> : mstd::type_identity<R(Args...)> { };
81 template <typename R, typename... Args>
82 struct unqualify_fn<R(Args...) volatile> : mstd::type_identity<R(Args...)> { };
83 template <typename R, typename... Args>
84 struct unqualify_fn<R(Args...) volatile &> : mstd::type_identity<R(Args...)> { };
85 template <typename R, typename... Args>
86 struct unqualify_fn<R(Args...) const volatile> : mstd::type_identity<R(Args...)> { };
87 template <typename R, typename... Args>
88 struct 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 */
92 template <typename R, typename... Args>
93 struct unqualify_fn<R(Args...) noexcept> : mstd::type_identity<R(Args...)> { };
94 template <typename R, typename... Args>
95 struct unqualify_fn<R(Args...) & noexcept> : mstd::type_identity<R(Args...)> { };
96 template <typename R, typename... Args>
97 struct unqualify_fn<R(Args...) const noexcept> : mstd::type_identity<R(Args...)> { };
98 template <typename R, typename... Args>
99 struct unqualify_fn<R(Args...) const & noexcept> : mstd::type_identity<R(Args...)> { };
100 template <typename R, typename... Args>
101 struct unqualify_fn<R(Args...) volatile noexcept> : mstd::type_identity<R(Args...)> { };
102 template <typename R, typename... Args>
103 struct unqualify_fn<R(Args...) volatile & noexcept> : mstd::type_identity<R(Args...)> { };
104 template <typename R, typename... Args>
105 struct unqualify_fn<R(Args...) const volatile noexcept> : mstd::type_identity<R(Args...)> { };
106 template <typename R, typename... Args>
107 struct unqualify_fn<R(Args...) const volatile & noexcept> : mstd::type_identity<R(Args...)> { };
108 #endif
109 
110 template <typename T>
111 using unqualify_fn_t = typename unqualify_fn<T>::type;
112 
113 template <typename R, typename F, typename... Args, typename std::enable_if_t<!std::is_void<R>::value, int> = 0>
114 R invoke_r(F&& f, Args&&... args)
115 {
116  return mstd::invoke(std::forward<F>(f), std::forward<Args>(args)...);
117 }
118 
119 template <typename R, typename F, typename... Args, typename std::enable_if_t<std::is_void<R>::value, int> = 0>
120 R invoke_r(F&& f, Args&&... args)
121 {
122  mstd::invoke(std::forward<F>(f), std::forward<Args>(args)...);
123 }
124 
125 template<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 
134 struct [[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  */
310 template <typename R, typename... ArgTs>
311 class Callback<R(ArgTs...)> : private detail::CallbackBase {
312 public:
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 
604 private:
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
665 
666 template <typename R, typename... ArgTs>
667 void 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  */
677 template <typename R, typename... ArgTs>
678 Callback<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  */
688 template <typename R, typename... ArgTs>
689 Callback<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  */
699 template <typename R, typename... ArgTs>
700 Callback<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  */
711 template<typename T, typename U, typename R, typename... ArgTs>
712 Callback<R(ArgTs...)> callback(U *obj, R(T::*method)(ArgTs...)) noexcept
713 {
714  return Callback<R(ArgTs...)>(obj, method);
715 }
716 
717 template<typename T, typename U, typename R, typename... ArgTs>
718 Callback<R(ArgTs...)> callback(U *obj, R(T::*method)(ArgTs...) &) noexcept
719 {
720  return Callback<R(ArgTs...)>(obj, method);
721 }
722 
723 template<typename T, typename U, typename R, typename... ArgTs>
724 Callback<R(ArgTs...)> callback(const U *obj, R(T::*method)(ArgTs...) const) noexcept
725 {
726  return Callback<R(ArgTs...)>(obj, method);
727 }
728 
729 template<typename T, typename U, typename R, typename... ArgTs>
730 Callback<R(ArgTs...)> callback(const U *obj, R(T::*method)(ArgTs...) const &) noexcept
731 {
732  return Callback<R(ArgTs...)>(obj, method);
733 }
734 
735 template<typename T, typename U, typename R, typename... ArgTs>
736 Callback<R(ArgTs...)> callback(volatile U *obj, R(T::*method)(ArgTs...) volatile) noexcept
737 {
738  return Callback<R(ArgTs...)>(obj, method);
739 }
740 
741 template<typename T, typename U, typename R, typename... ArgTs>
742 Callback<R(ArgTs...)> callback(volatile U *obj, R(T::*method)(ArgTs...) volatile &) noexcept
743 {
744  return Callback<R(ArgTs...)>(obj, method);
745 }
746 
747 template<typename T, typename U, typename R, typename... ArgTs>
748 Callback<R(ArgTs...)> callback(const volatile U *obj, R(T::*method)(ArgTs...) const volatile) noexcept
749 {
750  return Callback<R(ArgTs...)>(obj, method);
751 }
752 
753 template<typename T, typename U, typename R, typename... ArgTs>
754 Callback<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  */
765 template <typename T, typename U, typename R, typename... ArgTs>
766 Callback<R(ArgTs...)> callback(R(*func)(T *, ArgTs...), U *arg) noexcept
767 {
768  return Callback<R(ArgTs...)>(func, arg);
769 }
770 
771 template <typename T, typename U, typename R, typename... ArgTs>
772 Callback<R(ArgTs...)> callback(R(*func)(const T *, ArgTs...), const U *arg) noexcept
773 {
774  return Callback<R(ArgTs...)>(func, arg);
775 }
776 
777 template <typename T, typename U, typename R, typename... ArgTs>
778 Callback<R(ArgTs...)> callback(R(*func)(volatile T *, ArgTs...), volatile U *arg) noexcept
779 {
780  return Callback<R(ArgTs...)>(func, arg);
781 }
782 
783 template <typename T, typename U, typename R, typename... ArgTs>
784 Callback<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  */
793 template <typename F>
795 callback(F &&f)
796 {
798 }
799 
800 #if __cplusplus >= 201703 || __cpp_deduction_guides >= 201703
801 /* Deduction guides that can replace callback() helper */
802 template <typename R, typename... Args>
803 Callback(R(*)(Args...)) -> Callback<R(Args...)>;
804 template <typename F>
806 template <typename T, typename U, typename R, typename... ArgTs>
807 Callback(U *obj, R(T::*method)(ArgTs...)) -> Callback<R(ArgTs...)>;
808 template <typename T, typename U, typename R, typename... ArgTs>
809 Callback(U *obj, R(T::*method)(ArgTs...) &) -> Callback<R(ArgTs...)>;
810 template <typename T, typename U, typename R, typename... ArgTs>
811 Callback(const U *obj, R(T::*method)(ArgTs...) const) -> Callback<R(ArgTs...)>;
812 template <typename T, typename U, typename R, typename... ArgTs>
813 Callback(const U *obj, R(T::*method)(ArgTs...) const &) -> Callback<R(ArgTs...)>;
814 template <typename T, typename U, typename R, typename... ArgTs>
815 Callback(volatile U *obj, R(T::*method)(ArgTs...) volatile) -> Callback<R(ArgTs...)>;
816 template <typename T, typename U, typename R, typename... ArgTs>
817 Callback(volatile U *obj, R(T::*method)(ArgTs...) volatile &) -> Callback<R(ArgTs...)>;
818 template <typename T, typename U, typename R, typename... ArgTs>
819 Callback(const volatile U *obj, R(T::*method)(ArgTs...) const volatile) -> Callback<R(ArgTs...)>;
820 template <typename T, typename U, typename R, typename... ArgTs>
821 Callback(const volatile U *obj, R(T::*method)(ArgTs...) const volatile &) -> Callback<R(ArgTs...)>;
822 template <typename T, typename U, typename R, typename... ArgTs>
823 Callback(R(*func)(T *, ArgTs...), U *arg) -> Callback<R(ArgTs...)>;
824 template <typename T, typename U, typename R, typename... ArgTs>
825 Callback(R(*func)(const T *, ArgTs...), const U *arg) -> Callback<R(ArgTs...)>;
826 template <typename T, typename U, typename R, typename... ArgTs>
827 Callback(R(*func)(volatile T *, ArgTs...), volatile U *arg) -> Callback<R(ArgTs...)>;
828 template <typename T, typename U, typename R, typename... ArgTs>
829 Callback(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
R call(ArgTs...args) const
Call the attached function.
Definition: Callback.h:504
Callback(std::nullptr_t) noexcept
Create an empty Callback.
Definition: Callback.h:321
static R thunk(void *func, ArgTs...args)
Static thunk for passing as C-style function.
Definition: Callback.h:599
Callback(F f)
Create a Callback with a function object.
Definition: Callback.h:379
Callback() noexcept
Create an empty Callback.
Definition: Callback.h:317
Callback(Fn func, BoundArg arg)
Create a Callback with a static function and bound pointer.
Definition: Callback.h:363
friend bool operator==(std::nullptr_t, const Callback &f) noexcept
Test for emptiness.
Definition: Callback.h:563
Callback & operator=(std::nullptr_t) noexcept
Empty a callback.
Definition: Callback.h:494
friend bool operator!=(const Callback &f, std::nullptr_t) noexcept
Test for non-emptiness.
Definition: Callback.h:581
friend bool operator!=(std::nullptr_t, const Callback &f) noexcept
Test for non-emptiness.
Definition: Callback.h:588
Callback & operator=(F &&f)
Assign a callback.
Definition: Callback.h:472
Callback< detail::unqualify_fn_t< detail::member_type_t< decltype(&mstd::remove_cvref_t< F >::operator())> > > callback(F &&f)
Create a Create a callback class with type inferred from the arguments.
Definition: Callback.h:795
void operator!=(const SafeBool< T > &lhs, const SafeBool< U > &rhs)
Avoid conversion to bool between different classes.
Definition: SafeBool.h:142
#define MBED_ASSERT(expr)
MBED_ASSERT Declare runtime assertions: results in runtime error if condition is false.
Definition: mbed_assert.h:65
void operator==(const SafeBool< T > &lhs, const SafeBool< U > &rhs)
Avoid conversion to bool between different classes.
Definition: SafeBool.h:130
void swap(Callback &that) noexcept
Swap a callback.
Definition: Callback.h:416
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 class based on template specialization.
Definition: Callback.h:53
friend bool operator==(const Callback &f, std::nullptr_t) noexcept
Test for emptiness.
Definition: Callback.h:556
Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.