/* * Copyright (c) 2022, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /** * @file * This file defines OpenThread `Callback` class. */ #ifndef CALLBACK_HPP_ #define CALLBACK_HPP_ #include "openthread-core-config.h" #include #include "common/type_traits.hpp" namespace ot { /** * Specifies the context argument position in a callback function pointer. * */ enum CallbackContextPosition : uint8_t { kContextAsFirstArg, ///< Context is the first argument. kContextAsLastArg, ///< Context is the last argument. }; /** * Is the base class for `Callback` (a function pointer handler and a `void *` context). * * @tparam HandlerType The handler function pointer type. * */ template class CallbackBase { public: /** * Clears the `Callback` by setting the handler function pointer to `nullptr`. * */ void Clear(void) { mHandler = nullptr; } /** * Sets the callback handler function pointer and its associated context. * * @param[in] aHandler The handler function pointer. * @param[in] aContext The context associated with handler. * */ void Set(HandlerType aHandler, void *aContext) { mHandler = aHandler; mContext = aContext; } /** * Indicates whether or not the callback is set (not `nullptr`). * * @retval TRUE The handler is set. * @retval FALSE The handler is not set. * */ bool IsSet(void) const { return (mHandler != nullptr); } /** * Returns the handler function pointer. * * @returns The handler function pointer. * */ HandlerType GetHandler(void) const { return mHandler; } /** * Returns the context associated with callback. * * @returns The context. * */ void *GetContext(void) const { return mContext; } /** * Indicates whether the callback matches a given handler function pointer and context. * * @param[in] aHandler The handler function pointer to compare with. * @param[in] aContext The context associated with handler. * * @retval TRUE The callback matches @p aHandler and @p aContext. * @retval FALSE The callback does not match @p aHandler and @p aContext. * */ bool Matches(HandlerType aHandler, void *aContext) const { return (mHandler == aHandler) && (mContext == aContext); } protected: CallbackBase(void) : mHandler(nullptr) , mContext(nullptr) { } HandlerType mHandler; void *mContext; }; /** * Represents a `Callback` (a function pointer handler and a `void *` context). * * The context is passed as one of the arguments to the function pointer handler when invoked. * * The `Callback` provides two specializations based on `CallbackContextPosition` in the function pointer, i.e., whether * it is passed as the first argument or as the last argument. * * The `CallbackContextPosition` template parameter is automatically determined at compile-time based on the given * `HandlerType`. So user can simply use `Callback`. The `Invoke()` method will properly pass the context * to the function handler. * * @tparam HandlerType The function pointer handler type. * @tparam CallbackContextPosition Context position (first or last). Automatically determined at compile-time. * */ template ::Type, void *>::kValue ? kContextAsFirstArg : kContextAsLastArg)> class Callback { }; // Specialization for `kContextAsLastArg` template class Callback : public CallbackBase { using CallbackBase::mHandler; using CallbackBase::mContext; public: using ReturnType = typename TypeTraits::ReturnTypeOf::Type; ///< Return type of `HandlerType`. static constexpr CallbackContextPosition kContextPosition = kContextAsLastArg; ///< Context position. /** * Initializes `Callback` as empty (`nullptr` handler function pointer). * */ Callback(void) = default; /** * Invokes the callback handler. * * The caller MUST ensure that callback is set (`IsSet()` returns `true`) before calling this method. * * @param[in] aArgs The args to pass to the callback handler. * * @returns The return value from handler. * */ template ReturnType Invoke(Args &&...aArgs) const { return mHandler(static_cast(aArgs)..., mContext); } /** * Invokes the callback handler if it is set. * * The method MUST be used when the handler function returns `void`. * * @param[in] aArgs The args to pass to the callback handler. * */ template void InvokeIfSet(Args &&...aArgs) const { static_assert(TypeTraits::IsSame::kValue, "InvokeIfSet() MUST be used with `void` returning handler"); if (mHandler != nullptr) { Invoke(static_cast(aArgs)...); } } }; // Specialization for `kContextAsFirstArg` template class Callback : public CallbackBase { using CallbackBase::mHandler; using CallbackBase::mContext; public: using ReturnType = typename TypeTraits::ReturnTypeOf::Type; static constexpr CallbackContextPosition kContextPosition = kContextAsFirstArg; Callback(void) = default; template ReturnType Invoke(Args &&...aArgs) const { return mHandler(mContext, static_cast(aArgs)...); } template void InvokeIfSet(Args &&...aArgs) const { static_assert(TypeTraits::IsSame::kValue, "InvokeIfSet() MUST be used with `void` returning handler"); if (mHandler != nullptr) { Invoke(static_cast(aArgs)...); } } }; } // namespace ot #endif // CALLBACK_HPP_