/* mbed Microcontroller Library * Copyright (c) 2006-2019 ARM Limited * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef MBED_CALLBACK_H #define MBED_CALLBACK_H #include #include #include #include "platform/mbed_assert.h" #include "platform/mbed_toolchain.h" namespace mbed { /** \addtogroup platform-public-api */ /** @{*/ /** * \defgroup platform_Callback Callback class * @{ */ /** Callback class based on template specialization * * @note Synchronization level: Not protected */ template class Callback; // Internal sfinae declarations // // These are used to eliminate overloads based on type attributes // 1. Does a function object have a call operator // 2. Does a function object fit in the available storage // // These eliminations are handled cleanly by the compiler and avoid // massive and misleading error messages when confronted with an // invalid type (or worse, runtime failures) namespace detail { struct nil {}; template struct enable_if { typedef R type; }; template struct enable_if {}; template struct is_type { static const bool value = true; }; } #define MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, M) \ typename detail::enable_if< \ detail::is_type::value && \ sizeof(F) <= sizeof(uintptr_t) \ >::type = detail::nil() /** Callback class based on template specialization * * @note Synchronization level: Not protected */ template class Callback { public: /** Create a Callback with a static function * @param func Static function to attach */ Callback(R(*func)(ArgTs...) = 0) { if (!func) { memset(this, 0, sizeof(Callback)); } else { generate(func); } } /** Attach a Callback * @param func The Callback to attach */ Callback(const Callback &func) { memset(this, 0, sizeof(Callback)); if (func._ops) { func._ops->move(this, &func); } _ops = func._ops; } /** Create a Callback with a member function * @param obj Pointer to object to invoke member function on * @param method Member function to attach */ template Callback(U *obj, R(T::*method)(ArgTs...)) { generate(method_context(obj, method)); } /** Create a Callback with a member function * @param obj Pointer to object to invoke member function on * @param method Member function to attach */ template Callback(const U *obj, R(T::*method)(ArgTs...) const) { generate(method_context(obj, method)); } /** Create a Callback with a member function * @param obj Pointer to object to invoke member function on * @param method Member function to attach */ template Callback(volatile U *obj, R(T::*method)(ArgTs...) volatile) { generate(method_context(obj, method)); } /** Create a Callback with a member function * @param obj Pointer to object to invoke member function on * @param method Member function to attach */ template Callback(const volatile U *obj, R(T::*method)(ArgTs...) const volatile) { generate(method_context(obj, method)); } /** Create a Callback with a static function and bound pointer * @param func Static function to attach * @param arg Pointer argument to function */ template Callback(R(*func)(T *, ArgTs...), U *arg) { generate(function_context(func, arg)); } /** Create a Callback with a static function and bound pointer * @param func Static function to attach * @param arg Pointer argument to function */ template Callback(R(*func)(const T *, ArgTs...), const U *arg) { generate(function_context(func, arg)); } /** Create a Callback with a static function and bound pointer * @param func Static function to attach * @param arg Pointer argument to function */ template Callback(R(*func)(volatile T *, ArgTs...), volatile U *arg) { generate(function_context(func, arg)); } /** Create a Callback with a static function and bound pointer * @param func Static function to attach * @param arg Pointer argument to function */ template Callback(R(*func)(const volatile T *, ArgTs...), const volatile U *arg) { generate(function_context(func, arg)); } /** Create a Callback with a function object * @param f Function object to attach * @note The function object is limited to a single word of storage */ template Callback(F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R(F::*)(ArgTs...))) { generate(f); } /** Create a Callback with a function object * @param f Function object to attach * @note The function object is limited to a single word of storage */ template Callback(const F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R(F::*)(ArgTs...) const)) { generate(f); } /** Create a Callback with a function object * @param f Function object to attach * @note The function object is limited to a single word of storage */ template Callback(volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R(F::*)(ArgTs...) volatile)) { generate(f); } /** Create a Callback with a function object * @param f Function object to attach * @note The function object is limited to a single word of storage */ template Callback(const volatile F f, MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, R(F::*)(ArgTs...) const volatile)) { generate(f); } /** Destroy a callback */ ~Callback() { if (_ops) { _ops->dtor(this); } } /** Assign a callback */ Callback &operator=(const Callback &that) { if (this != &that) { this->~Callback(); new (this) Callback(that); } return *this; } /** Call the attached function */ R call(ArgTs... args) const { MBED_ASSERT(_ops); return _ops->call(this, args...); } /** Call the attached function */ R operator()(ArgTs... args) const { return call(args...); } /** Test if function has been attached */ operator bool() const { return _ops; } /** Test for equality */ friend bool operator==(const Callback &l, const Callback &r) { return memcmp(&l, &r, sizeof(Callback)) == 0; } /** Test for inequality */ friend bool operator!=(const Callback &l, const Callback &r) { return !(l == r); } /** Static thunk for passing as C-style function * @param func Callback to call passed as void pointer * @param args Arguments to be called with function func * @return the value as determined by func which is of * type and determined by the signature of func */ static R thunk(void *func, ArgTs... args) { return static_cast(func)->call(args...); } private: // Stored as pointer to function and pointer to optional object // Function pointer is stored as union of possible function types // to guarantee proper size and alignment struct _class; union { void (*_staticfunc)(ArgTs...); void (*_boundfunc)(_class *, ArgTs...); void (_class::*_methodfunc)(ArgTs...); } _func; void *_obj; // Dynamically dispatched operations const struct ops { R(*call)(const void *, ArgTs...); void (*move)(void *, const void *); void (*dtor)(void *); } *_ops; // Generate operations for function object template void generate(const F &f) { static const ops ops = { &Callback::function_call, &Callback::function_move, &Callback::function_dtor, }; MBED_STATIC_ASSERT(sizeof(Callback) - sizeof(_ops) >= sizeof(F), "Type F must not exceed the size of the Callback class"); memset(this, 0, sizeof(Callback)); new (this) F(f); _ops = &ops; } // Function attributes template static R function_call(const void *p, ArgTs... args) { return (*(F *)p)(args...); } template static void function_move(void *d, const void *p) { new (d) F(*(F *)p); } template static void function_dtor(void *p) { ((F *)p)->~F(); } // Wrappers for functions with context template struct method_context { M method; O *obj; method_context(O *obj, M method) : method(method), obj(obj) {} R operator()(ArgTs... args) const { return (obj->*method)(args...); } }; template struct function_context { F func; A *arg; function_context(F func, A *arg) : func(func), arg(arg) {} R operator()(ArgTs... args) const { return func(arg, args...); } }; }; // Internally used event type typedef Callback event_callback_t; /** Create a callback class with type inferred from the arguments * * @param func Static function to attach * @return Callback with inferred type */ template Callback callback(R(*func)(ArgTs...) = 0) { return Callback(func); } /** Create a callback class with type inferred from the arguments * * @param func Static function to attach * @return Callback with inferred type */ template Callback callback(const Callback &func) { return Callback(func); } /** Create a callback class with type inferred from the arguments * * @param obj Optional pointer to object to bind to function * @param method Member function to attach * @return Callback with inferred type */ template Callback callback(U *obj, R(T::*method)(ArgTs...)) { return Callback(obj, method); } /** Create a callback class with type inferred from the arguments * * @param obj Optional pointer to object to bind to function * @param method Member function to attach * @return Callback with inferred type */ template Callback callback(const U *obj, R(T::*method)(ArgTs...) const) { return Callback(obj, method); } /** Create a callback class with type inferred from the arguments * * @param obj Optional pointer to object to bind to function * @param method Member function to attach * @return Callback with inferred type */ template Callback callback(volatile U *obj, R(T::*method)(ArgTs...) volatile) { return Callback(obj, method); } /** Create a callback class with type inferred from the arguments * * @param obj Optional pointer to object to bind to function * @param method Member function to attach * @return Callback with inferred type */ template Callback callback(const volatile U *obj, R(T::*method)(ArgTs...) const volatile) { return Callback(obj, method); } /** Create a callback class with type inferred from the arguments * * @param func Static function to attach * @param arg Pointer argument to function * @return Callback with inferred type */ template Callback callback(R(*func)(T *, ArgTs...), U *arg) { return Callback(func, arg); } /** Create a callback class with type inferred from the arguments * * @param func Static function to attach * @param arg Pointer argument to function * @return Callback with inferred type */ template Callback callback(R(*func)(const T *, ArgTs...), const U *arg) { return Callback(func, arg); } /** Create a callback class with type inferred from the arguments * * @param func Static function to attach * @param arg Pointer argument to function * @return Callback with inferred type */ template Callback callback(R(*func)(volatile T *, ArgTs...), volatile U *arg) { return Callback(func, arg); } /** Create a callback class with type inferred from the arguments * * @param func Static function to attach * @param arg Pointer argument to function * @return Callback with inferred type */ template Callback callback(R(*func)(const volatile T *, ArgTs...), const volatile U *arg) { return Callback(func, arg); } /**@}*/ /**@}*/ } // namespace mbed #endif