xref: /aosp_15_r20/external/openthread/src/core/common/callback.hpp (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
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  */
49 enum CallbackContextPosition : uint8_t
50 {
51     kContextAsFirstArg, ///< Context is the first argument.
52     kContextAsLastArg,  ///< Context is the last argument.
53 };
54 
55 /**
56  * Is the base class for `Callback` (a function pointer handler and a `void *` context).
57  *
58  * @tparam HandlerType    The handler function pointer type.
59  *
60  */
61 template <typename HandlerType> class CallbackBase
62 {
63 public:
64     /**
65      * Clears the `Callback` by setting the handler function pointer to `nullptr`.
66      *
67      */
Clear(void)68     void Clear(void) { mHandler = nullptr; }
69 
70     /**
71      * Sets the callback handler function pointer and its associated context.
72      *
73      * @param[in] aHandler   The handler function pointer.
74      * @param[in] aContext   The context associated with handler.
75      *
76      */
Set(HandlerType aHandler,void * aContext)77     void Set(HandlerType aHandler, void *aContext)
78     {
79         mHandler = aHandler;
80         mContext = aContext;
81     }
82 
83     /**
84      * Indicates whether or not the callback is set (not `nullptr`).
85      *
86      * @retval TRUE   The handler is set.
87      * @retval FALSE  The handler is not set.
88      *
89      */
IsSet(void) const90     bool IsSet(void) const { return (mHandler != nullptr); }
91 
92     /**
93      * Returns the handler function pointer.
94      *
95      * @returns The handler function pointer.
96      *
97      */
GetHandler(void) const98     HandlerType GetHandler(void) const { return mHandler; }
99 
100     /**
101      * Returns the context associated with callback.
102      *
103      * @returns The context.
104      *
105      */
GetContext(void) const106     void *GetContext(void) const { return mContext; }
107 
108     /**
109      * Indicates whether the callback matches a given handler function pointer and context.
110      *
111      * @param[in] aHandler   The handler function pointer to compare with.
112      * @param[in] aContext   The context associated with handler.
113      *
114      * @retval TRUE   The callback matches @p aHandler and @p aContext.
115      * @retval FALSE  The callback does not match @p aHandler and @p aContext.
116      *
117      */
Matches(HandlerType aHandler,void * aContext) const118     bool Matches(HandlerType aHandler, void *aContext) const
119     {
120         return (mHandler == aHandler) && (mContext == aContext);
121     }
122 
123 protected:
CallbackBase(void)124     CallbackBase(void)
125         : mHandler(nullptr)
126         , mContext(nullptr)
127     {
128     }
129 
130     HandlerType mHandler;
131     void       *mContext;
132 };
133 
134 /**
135  * Represents a `Callback` (a function pointer handler and a `void *` context).
136  *
137  * The context is passed as one of the arguments to the function pointer handler when invoked.
138  *
139  * The `Callback` provides two specializations based on `CallbackContextPosition` in the function pointer, i.e., whether
140  * it is passed as the first argument or as the last argument.
141  *
142  * The `CallbackContextPosition` template parameter is automatically determined at compile-time based on the given
143  * `HandlerType`. So user can simply use `Callback<HandlerType>`. The `Invoke()` method will properly pass the context
144  * to the function handler.
145  *
146  * @tparam  HandlerType                The function pointer handler type.
147  * @tparam  CallbackContextPosition    Context position (first or last). Automatically determined at compile-time.
148  *
149  */
150 template <typename HandlerType,
151           CallbackContextPosition =
152               (TypeTraits::IsSame<typename TypeTraits::FirstArgTypeOf<HandlerType>::Type, void *>::kValue
153                    ? kContextAsFirstArg
154                    : kContextAsLastArg)>
155 class Callback
156 {
157 };
158 
159 // Specialization for `kContextAsLastArg`
160 template <typename HandlerType> class Callback<HandlerType, kContextAsLastArg> : public CallbackBase<HandlerType>
161 {
162     using CallbackBase<HandlerType>::mHandler;
163     using CallbackBase<HandlerType>::mContext;
164 
165 public:
166     using ReturnType = typename TypeTraits::ReturnTypeOf<HandlerType>::Type; ///< Return type of `HandlerType`.
167 
168     static constexpr CallbackContextPosition kContextPosition = kContextAsLastArg; ///< Context position.
169 
170     /**
171      * Initializes `Callback` as empty (`nullptr` handler function pointer).
172      *
173      */
174     Callback(void) = default;
175 
176     /**
177      * Invokes the callback handler.
178      *
179      * The caller MUST ensure that callback is set (`IsSet()` returns `true`) before calling this method.
180      *
181      * @param[in] aArgs   The args to pass to the callback handler.
182      *
183      * @returns The return value from handler.
184      *
185      */
Invoke(Args &&...aArgs) const186     template <typename... Args> ReturnType Invoke(Args &&...aArgs) const
187     {
188         return mHandler(static_cast<Args &&>(aArgs)..., mContext);
189     }
190 
191     /**
192      * Invokes the callback handler if it is set.
193      *
194      * The method MUST be used when the handler function returns `void`.
195      *
196      * @param[in] aArgs   The args to pass to the callback handler.
197      *
198      */
InvokeIfSet(Args &&...aArgs) const199     template <typename... Args> void InvokeIfSet(Args &&...aArgs) const
200     {
201         static_assert(TypeTraits::IsSame<ReturnType, void>::kValue,
202                       "InvokeIfSet() MUST be used with `void` returning handler");
203 
204         if (mHandler != nullptr)
205         {
206             Invoke(static_cast<Args &&>(aArgs)...);
207         }
208     }
209 
210     /**
211      * Invokes the callback handler if it is set and clears it.
212      *
213      * The method MUST be used when the handler function returns `void`.
214      *
215      * The callback is cleared first before invoking its handler to allow it to be set again from the handler
216      * implementation.
217      *
218      * @param[in] aArgs   The args to pass to the callback handler.
219      *
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      *
269      */
InvokeAndClearIfSet(Args &&...aArgs)270     template <typename... Args> void InvokeAndClearIfSet(Args &&...aArgs)
271     {
272         Callback<HandlerType, kContextAsFirstArg> callbackCopy = *this;
273 
274         CallbackBase<HandlerType>::Clear();
275         callbackCopy.InvokeIfSet(static_cast<Args &&>(aArgs)...);
276     }
277 };
278 
279 } // namespace ot
280 
281 #endif // CALLBACK_HPP_
282