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  * This enumeration 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  * This class 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      * This method clears the `Callback` by setting the handler function pointer to `nullptr`.
66      *
67      */
Clear(void)68     void Clear(void) { mHandler = nullptr; }
69 
70     /**
71      * This method 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      * This method 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      * This method 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      * This method 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      * This method 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  * This class 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      * This constructor initializes `Callback` as empty (`nullptr` handler function pointer).
172      *
173      */
174     Callback(void) = default;
175 
176     /**
177      * This method 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      * This method 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 // Specialization for `kContextAsFirstArg`
212 template <typename HandlerType> class Callback<HandlerType, kContextAsFirstArg> : public CallbackBase<HandlerType>
213 {
214     using CallbackBase<HandlerType>::mHandler;
215     using CallbackBase<HandlerType>::mContext;
216 
217 public:
218     using ReturnType = typename TypeTraits::ReturnTypeOf<HandlerType>::Type;
219 
220     static constexpr CallbackContextPosition kContextPosition = kContextAsFirstArg;
221 
222     Callback(void) = default;
223 
Invoke(Args &&...aArgs) const224     template <typename... Args> ReturnType Invoke(Args &&...aArgs) const
225     {
226         return mHandler(mContext, static_cast<Args &&>(aArgs)...);
227     }
228 
InvokeIfSet(Args &&...aArgs) const229     template <typename... Args> void InvokeIfSet(Args &&...aArgs) const
230     {
231         static_assert(TypeTraits::IsSame<ReturnType, void>::kValue,
232                       "InvokeIfSet() MUST be used with `void` returning handler");
233 
234         if (mHandler != nullptr)
235         {
236             Invoke(static_cast<Args &&>(aArgs)...);
237         }
238     }
239 };
240 
241 } // namespace ot
242 
243 #endif // CALLBACK_HPP_
244