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