• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2022, The OpenThread Authors.
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. Neither the name of the copyright holder nor the
13  *     names of its contributors may be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  *  POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /**
30  * @file
31  *  This file defines OpenThread `Callback` class.
32  */
33 
34 #ifndef CALLBACK_HPP_
35 #define CALLBACK_HPP_
36 
37 #include "openthread-core-config.h"
38 
39 #include <stdint.h>
40 
41 #include "common/type_traits.hpp"
42 
43 namespace ot {
44 
45 /**
46  * Specifies the context argument position in a callback function pointer.
47  */
48 enum CallbackContextPosition : uint8_t
49 {
50     kContextAsFirstArg, ///< Context is the first argument.
51     kContextAsLastArg,  ///< Context is the last argument.
52 };
53 
54 /**
55  * Is the base class for `Callback` (a function pointer handler and a `void *` context).
56  *
57  * @tparam HandlerType    The handler function pointer type.
58  */
59 template <typename HandlerType> class CallbackBase
60 {
61 public:
62     /**
63      * Clears the `Callback` by setting the handler function pointer to `nullptr`.
64      */
Clear(void)65     void Clear(void) { mHandler = nullptr; }
66 
67     /**
68      * Sets the callback handler function pointer and its associated context.
69      *
70      * @param[in] aHandler   The handler function pointer.
71      * @param[in] aContext   The context associated with handler.
72      */
Set(HandlerType aHandler,void * aContext)73     void Set(HandlerType aHandler, void *aContext)
74     {
75         mHandler = aHandler;
76         mContext = aContext;
77     }
78 
79     /**
80      * Indicates whether or not the callback is set (not `nullptr`).
81      *
82      * @retval TRUE   The handler is set.
83      * @retval FALSE  The handler is not set.
84      */
IsSet(void) const85     bool IsSet(void) const { return (mHandler != nullptr); }
86 
87     /**
88      * Returns the handler function pointer.
89      *
90      * @returns The handler function pointer.
91      */
GetHandler(void) const92     HandlerType GetHandler(void) const { return mHandler; }
93 
94     /**
95      * Returns the context associated with callback.
96      *
97      * @returns The context.
98      */
GetContext(void) const99     void *GetContext(void) const { return mContext; }
100 
101     /**
102      * Indicates whether the callback matches a given handler function pointer and context.
103      *
104      * @param[in] aHandler   The handler function pointer to compare with.
105      * @param[in] aContext   The context associated with handler.
106      *
107      * @retval TRUE   The callback matches @p aHandler and @p aContext.
108      * @retval FALSE  The callback does not match @p aHandler and @p aContext.
109      */
Matches(HandlerType aHandler,void * aContext) const110     bool Matches(HandlerType aHandler, void *aContext) const
111     {
112         return (mHandler == aHandler) && (mContext == aContext);
113     }
114 
115     /**
116      * Overloads operator `==` to evaluate whether or not two given `Callback` objects are equal.
117      *
118      * @param[in] aOtherCallback   The callback to compare with.
119      *
120      * @retval TRUE  The two callbacks are equal.
121      * @retval FALSE The two callbacks are not equal.
122      */
operator ==(const CallbackBase & aOtherCallback) const123     bool operator==(const CallbackBase &aOtherCallback) const
124     {
125         return Matches(aOtherCallback.mHandler, aOtherCallback.mContext);
126     }
127 
128 protected:
CallbackBase(void)129     CallbackBase(void)
130         : mHandler(nullptr)
131         , mContext(nullptr)
132     {
133     }
134 
135     HandlerType mHandler;
136     void       *mContext;
137 };
138 
139 /**
140  * Represents a `Callback` (a function pointer handler and a `void *` context).
141  *
142  * The context is passed as one of the arguments to the function pointer handler when invoked.
143  *
144  * The `Callback` provides two specializations based on `CallbackContextPosition` in the function pointer, i.e., whether
145  * it is passed as the first argument or as the last argument.
146  *
147  * The `CallbackContextPosition` template parameter is automatically determined at compile-time based on the given
148  * `HandlerType`. So user can simply use `Callback<HandlerType>`. The `Invoke()` method will properly pass the context
149  * to the function handler.
150  *
151  * @tparam  HandlerType                The function pointer handler type.
152  * @tparam  CallbackContextPosition    Context position (first or last). Automatically determined at compile-time.
153  */
154 template <typename HandlerType,
155           CallbackContextPosition =
156               (TypeTraits::IsSame<typename TypeTraits::FirstArgTypeOf<HandlerType>::Type, void *>::kValue
157                    ? kContextAsFirstArg
158                    : kContextAsLastArg)>
159 class Callback
160 {
161 };
162 
163 // Specialization for `kContextAsLastArg`
164 template <typename HandlerType> class Callback<HandlerType, kContextAsLastArg> : public CallbackBase<HandlerType>
165 {
166     using CallbackBase<HandlerType>::mHandler;
167     using CallbackBase<HandlerType>::mContext;
168 
169 public:
170     using ReturnType = typename TypeTraits::ReturnTypeOf<HandlerType>::Type; ///< Return type of `HandlerType`.
171 
172     static constexpr CallbackContextPosition kContextPosition = kContextAsLastArg; ///< Context position.
173 
174     /**
175      * Initializes `Callback` as empty (`nullptr` handler function pointer).
176      */
177     Callback(void) = default;
178 
179     /**
180      * Invokes the callback handler.
181      *
182      * The caller MUST ensure that callback is set (`IsSet()` returns `true`) before calling this method.
183      *
184      * @param[in] aArgs   The args to pass to the callback handler.
185      *
186      * @returns The return value from handler.
187      */
Invoke(Args &&...aArgs) const188     template <typename... Args> ReturnType Invoke(Args &&...aArgs) const
189     {
190         return mHandler(static_cast<Args &&>(aArgs)..., mContext);
191     }
192 
193     /**
194      * Invokes the callback handler if it is set.
195      *
196      * The method MUST be used when the handler function returns `void`.
197      *
198      * @param[in] aArgs   The args to pass to the callback handler.
199      */
InvokeIfSet(Args &&...aArgs) const200     template <typename... Args> void InvokeIfSet(Args &&...aArgs) const
201     {
202         static_assert(TypeTraits::IsSame<ReturnType, void>::kValue,
203                       "InvokeIfSet() MUST be used with `void` returning handler");
204 
205         if (mHandler != nullptr)
206         {
207             Invoke(static_cast<Args &&>(aArgs)...);
208         }
209     }
210 
211     /**
212      * Invokes the callback handler if it is set and clears it.
213      *
214      * The method MUST be used when the handler function returns `void`.
215      *
216      * The callback is cleared first before invoking its handler to allow it to be set again from the handler
217      * implementation.
218      *
219      * @param[in] aArgs   The args to pass to the callback handler.
220      */
InvokeAndClearIfSet(Args &&...aArgs)221     template <typename... Args> void InvokeAndClearIfSet(Args &&...aArgs)
222     {
223         Callback<HandlerType, kContextAsLastArg> callbackCopy = *this;
224 
225         CallbackBase<HandlerType>::Clear();
226         callbackCopy.InvokeIfSet(static_cast<Args &&>(aArgs)...);
227     }
228 };
229 
230 // Specialization for `kContextAsFirstArg`
231 template <typename HandlerType> class Callback<HandlerType, kContextAsFirstArg> : public CallbackBase<HandlerType>
232 {
233     using CallbackBase<HandlerType>::mHandler;
234     using CallbackBase<HandlerType>::mContext;
235 
236 public:
237     using ReturnType = typename TypeTraits::ReturnTypeOf<HandlerType>::Type;
238 
239     static constexpr CallbackContextPosition kContextPosition = kContextAsFirstArg;
240 
241     Callback(void) = default;
242 
Invoke(Args &&...aArgs) const243     template <typename... Args> ReturnType Invoke(Args &&...aArgs) const
244     {
245         return mHandler(mContext, static_cast<Args &&>(aArgs)...);
246     }
247 
InvokeIfSet(Args &&...aArgs) const248     template <typename... Args> void InvokeIfSet(Args &&...aArgs) const
249     {
250         static_assert(TypeTraits::IsSame<ReturnType, void>::kValue,
251                       "InvokeIfSet() MUST be used with `void` returning handler");
252 
253         if (mHandler != nullptr)
254         {
255             Invoke(static_cast<Args &&>(aArgs)...);
256         }
257     }
258 
259     /**
260      * Invokes the callback handler if it is set and clears it.
261      *
262      * The method MUST be used when the handler function returns `void`.
263      *
264      * The callback is cleared first before invoking its handler to allow it to be set again from the handler
265      * implementation.
266      *
267      * @param[in] aArgs   The args to pass to the callback handler.
268      */
InvokeAndClearIfSet(Args &&...aArgs)269     template <typename... Args> void InvokeAndClearIfSet(Args &&...aArgs)
270     {
271         Callback<HandlerType, kContextAsFirstArg> callbackCopy = *this;
272 
273         CallbackBase<HandlerType>::Clear();
274         callbackCopy.InvokeIfSet(static_cast<Args &&>(aArgs)...);
275     }
276 };
277 
278 } // namespace ot
279 
280 #endif // CALLBACK_HPP_
281