1 /*
2  * Copyright (c) 2022 - 2024, Nordic Semiconductor ASA
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice, this
11  *    list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its
18  *    contributors may be used to endorse or promote products derived from this
19  *    software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #ifndef NRFX_UTILS_H__
35 #define NRFX_UTILS_H__
36 
37 #include "nrfx_utils_internal.h"
38 
39 /**
40  * @defgroup nrfx_utils Preprocessor utility macros
41  * @{
42  * @ingroup nrfx
43  * @brief Preprocessor utility macros.
44  */
45 
46 /**
47  * @brief Macro for inserting code depending on whether @p _flag exists and expands to 1 or not.
48  *
49  * To prevent the preprocessor from treating commas as argument
50  * separators, the @p _if_1_code and @p _else_code expressions must be
51  * inside brackets/parentheses: <tt>()</tt>. These are stripped away
52  * during macro expansion.
53  *
54  * Example:
55  *
56  *     NRFX_COND_CODE_1(CONFIG_FLAG, (uint32_t x;), (there_is_no_flag();))
57  *
58  * If @p CONFIG_FLAG is defined to 1, this expands to:
59  *
60  *     uint32_t x;
61  *
62  * It expands to <tt>there_is_no_flag();</tt> otherwise.
63  *
64  * This could be used as an alternative to:
65  *
66  *     #if defined(CONFIG_FLAG) && (CONFIG_FLAG == 1)
67  *     #define MAYBE_DECLARE(x) uint32_t x
68  *     #else
69  *     #define MAYBE_DECLARE(x) there_is_no_flag()
70  *     #endif
71  *
72  *     MAYBE_DECLARE(x);
73  *
74  * However, the advantage of COND_CODE_1() is that code is resolved in
75  * place where it is used, while the @p \#if method defines @p
76  * MAYBE_DECLARE on two lines and requires it to be invoked again on a
77  * separate line. This makes COND_CODE_1() more concise and also
78  * sometimes more useful when used within another macro's expansion.
79  *
80  * @note @p _flag can be the result of preprocessor expansion,
81  *       however @p _if_1_code is only expanded if @p _flag expands
82  *       to the integer literal 1. Integer expressions that evaluate
83  *       to 1, e.g. after doing some arithmetic, will not work.
84  *
85  * @param[in] _flag      Evaluated flag
86  * @param[in] _if_1_code Result if @p _flag expands to 1; must be in parentheses
87  * @param[in] _else_code Result otherwise; must be in parentheses
88  */
89 #define NRFX_COND_CODE_1(_flag, _if_1_code, _else_code) \
90     _NRFX_COND_CODE_1(_flag, _if_1_code, _else_code)
91 
92 /**
93  * @brief Macro for inserting code depending on whether @p _flag exists and expands to 0 or not.
94  *
95  * This is like @ref NRFX_COND_CODE_1(), except that it tests whether @p _flag
96  * expands to the integer literal 0. It expands to @p _if_0_code if
97  * so, and @p _else_code otherwise; both of these must be enclosed in
98  * parentheses.
99  *
100  * @param[in] _flag      Evaluated flag
101  * @param[in] _if_0_code Result if @p _flag expands to 0; must be in parentheses
102  * @param[in] _else_code Result otherwise; must be in parentheses
103  */
104 #define NRFX_COND_CODE_0(_flag, _if_0_code, _else_code) \
105     _NRFX_COND_CODE_0(_flag, _if_0_code, _else_code)
106 
107 /**
108  * @brief Macro for checking for macro definition in compiler-visible expressions
109  *
110  * It has the effect of taking a macro value that may be defined to "1"
111  * or may not be defined at all and turning it into a literal
112  * expression that can be handled by the C compiler instead of just
113  * the preprocessor.
114  *
115  * That is, it works similarly to <tt>\#if defined(CONFIG_FOO)</tt>
116  * except that its expansion is a C expression. Thus, much <tt>\#ifdef</tt>
117  * usage can be replaced with equivalents like:
118  *
119  *     if (IS_ENABLED(CONFIG_FOO)) {
120  *             do_something_with_foo
121  *     }
122  *
123  * This is cleaner since the compiler can generate errors and warnings
124  * for @p do_something_with_foo even when @p CONFIG_FOO is undefined.
125  *
126  * @param[in] config_macro Macro to check
127  *
128  * @return 1 if @p config_macro is defined to 1, 0 otherwise (including
129  *         if @p config_macro is not defined)
130  */
131 #define NRFX_IS_ENABLED(config_macro) _NRFX_IS_ENABLED1(config_macro)
132 
133 /**
134  * @brief Macro for generating a sequence of code with configurable separator.
135  *
136  * Example:
137  *
138  *     #define FOO(i, _) MY_PWM ## i
139  *     { NRFX_LISTIFY(PWM_COUNT, FOO, (,)) }
140  *
141  * The above two lines expand to:
142  *
143  *    { MY_PWM0 , MY_PWM1 }
144  *
145  * @param[in] LEN The length of the sequence. Must be an integer literal less
146  *                than 255.
147  * @param[in] F   A macro function that accepts at least two arguments:
148  *                <tt>F(i, ...)</tt>. @p F is called repeatedly in the expansion.
149  *                Its first argument @p i is the index in the sequence, and
150  *                the variable list of arguments passed to LISTIFY are passed
151  *                through to @p F.
152  * @param[in] sep Separator (e.g. comma or semicolon). Must be in parentheses;
153  *            this is required to enable providing a comma as separator.
154  *
155  * @note Calling NRFX_LISTIFY with undefined arguments has undefined behavior.
156  */
157 #define NRFX_LISTIFY(LEN, F, sep, ...) \
158     NRFX_CONCAT_2(_NRFX_LISTIFY_, LEN)(F, sep, __VA_ARGS__)
159 
160 /**
161  * @brief Macro for checking if input argument is empty.
162  *
163  * Empty means that nothing is provided or provided value is resolved to nothing
164  * (e.g. empty define).
165  *
166  * Macro idea is taken from P99 which is under Apache 2.0 license and described by
167  * Jens Gustedt https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
168  *
169  * @param arg Argument.
170  *
171  * @retval 1 if argument is empty.
172  * @retval 0 if argument is not empty.
173  */
174 #define NRFX_IS_EMPTY(arg) _NRFX_IS_EMPTY(arg)
175 
176 /**
177  * @brief Macro for calculating number of arguments in the variable arguments list minus one.
178  *
179  * @param[in] ... List of arguments
180  *
181  * @return Number of variadic arguments in the argument list, minus one
182  */
183 #define NRFX_NUM_VA_ARGS_LESS_1(...) \
184         _NRFX_NUM_VA_ARGS_LESS_1_IMPL(__VA_ARGS__, 63, 62, 61, \
185                     60, 59, 58, 57, 56, 55, 54, 53, 52, 51, \
186                     50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \
187                     40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \
188                     30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \
189                     20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \
190                     10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ~)
191 
192 /**
193  * @brief Macro for concatenating multiple arguments.
194  *
195  * Support up to 8 arguments.
196  *
197  * @param[in] ... Arguments to concatenate.
198  */
199 #define NRFX_CONCAT(...) \
200     NRFX_CONCAT_2(_NRFX_CONCAT_, NRFX_NUM_VA_ARGS_LESS_1(__VA_ARGS__))(__VA_ARGS__)
201 
202 /**
203  * @brief Macro for checking if argument starts with opening round bracket and contains matching
204  *        closing bracket (parenthesis).
205  *
206  * @param[in] x Input argument.
207  *
208  * @retval 1 If input argument starts with opening bracket and contains closing bracket.
209  * @retval 0 If input argument does not match above mentioned condition.
210  */
211 #define NRFX_ARG_HAS_PARENTHESIS(x) _NRFX_GET_ARG3(_NRFX_EVAL(_NRFX_ARG_HAS_PARENTHESIS x, 1, 0))
212 
213 /**
214  * @brief Macro for calling a macro @p F on each provided argument with a given
215  *        separator between each call.
216  *
217  * Example:
218  *
219  *     #define F(x) int a##x
220  *     NRFX_FOR_EACH(F, (;), 4, 5, 6);
221  *
222  * This expands to:
223  *
224  *     int a4;
225  *     int a5;
226  *     int a6;
227  *
228  * @param F Macro to invoke
229  * @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
230  *            this is required to enable providing a comma as a separator.
231  * @param ... Variable argument list. The macro @p F is invoked as
232  *            <tt>F(element)</tt> for each element in the list.
233  */
234 #define NRFX_FOR_EACH(F, sep, ...) \
235     _NRFX_FOR_EACH(F, sep, NRFX_REVERSE_ARGS(__VA_ARGS__))
236 
237 /**
238  * @brief Call macro @p F on each provided argument, with the argument's index
239  *        as an additional parameter.
240  *
241  * This is like @ref NRFX_FOR_EACH(), except @p F should be a macro which takes two
242  * arguments: <tt>F(index, variable_arg)</tt>.
243  *
244  * Example:
245  *
246  *     #define F(idx, x) int a##idx = x
247  *     NRFX_FOR_EACH_IDX(F, (;), 4, 5, 6);
248  *
249  * This expands to:
250  *
251  *     int a0 = 4;
252  *     int a1 = 5;
253  *     int a2 = 6;
254  *
255  * @param F Macro to invoke
256  * @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
257  *            this is required to enable providing a comma as a separator.
258  * @param ... Variable argument list. The macro @p F is invoked as
259  *            <tt>F(index, element)</tt> for each element in the list.
260  */
261 #define NRFX_FOR_EACH_IDX(F, sep, ...) \
262     _NRFX_FOR_EACH_IDX(F, sep, NRFX_REVERSE_ARGS(__VA_ARGS__))
263 
264 /**
265  * @brief Macro for calling macro @p F on each provided argument, with an additional fixed
266  *        argument as a parameter.
267  *
268  * This is like @ref NRFX_FOR_EACH(), except @p F should be a macro which takes two
269  * arguments: <tt>F(variable_arg, fixed_arg)</tt>.
270  *
271  * Example:
272  *
273  *     static void func(int val, void *dev);
274  *     NRFX_FOR_EACH_FIXED_ARG(func, (;), dev, 4, 5, 6);
275  *
276  * This expands to:
277  *
278  *     func(4, dev);
279  *     func(5, dev);
280  *     func(6, dev);
281  *
282  * @param F Macro to invoke
283  * @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
284  *            this is required to enable providing a comma as a separator.
285  * @param fixed_arg Fixed argument passed to @p F as the second macro parameter.
286  * @param ... Variable argument list. The macro @p F is invoked as
287  *            <tt>F(element, fixed_arg)</tt> for each element in the list.
288  */
289 #define NRFX_FOR_EACH_FIXED_ARG(F, sep, fixed_arg, ...) \
290     _NRFX_FOR_EACH_FIXED_ARG(F, sep, fixed_arg, NRFX_REVERSE_ARGS(__VA_ARGS__))
291 
292 /**
293  * @brief Macro from calling macro @p F for each variable argument with an index and fixed
294  *        argument
295  *
296  * This is like the combination of @ref NRFX_FOR_EACH_IDX() with @ref NRFX_FOR_EACH_FIXED_ARG().
297  *
298  * Example:
299  *
300  *     #define F(idx, x, fixed_arg) int fixed_arg##idx = x
301  *     NRFX_FOR_EACH_IDX_FIXED_ARG(F, (;), a, 4, 5, 6);
302  *
303  * This expands to:
304  *
305  *     int a0 = 4;
306  *     int a1 = 5;
307  *     int a2 = 6;
308  *
309  * @param F Macro to invoke
310  * @param sep Separator (e.g. comma or semicolon). Must be in parentheses;
311  *            This is required to enable providing a comma as a separator.
312  * @param fixed_arg Fixed argument passed to @p F as the third macro parameter.
313  * @param ... Variable list of arguments. The macro @p F is invoked as
314  *            <tt>F(index, element, fixed_arg)</tt> for each element in
315  *            the list.
316  */
317 #define NRFX_FOR_EACH_IDX_FIXED_ARG(F, sep, fixed_arg, ...) \
318     _NRFX_FOR_EACH_IDX_FIXED_ARG(F, sep, fixed_arg, NRFX_REVERSE_ARGS(__VA_ARGS__))
319 
320 /**
321  * @brief Macro for reversing arguments order.
322  *
323  * @param ... Variable argument list.
324  *
325  * @return Input arguments in reversed order.
326  */
327 #define NRFX_REVERSE_ARGS(...) \
328     _NRFX_FOR_EACH_ENGINE(_NRFX_FOR_EACH_EXEC, (,), NRFX_EVAL, _, __VA_ARGS__)
329 
330 
331 /**
332  * @brief Macro for getting the highest value from input arguments.
333  *
334  * It is similar to @ref NRFX_MAX but accepts a variable number of arguments.
335  *
336  * @note Input arguments must be numeric variables.
337  *
338  * @param ... Variable argument list.
339  *
340  * @return Highest value from the input list.
341  */
342 #define NRFX_MAX_N(...) \
343     NRFX_EVAL(NRFX_FOR_EACH(_NRFX_MAX_P1, (), __VA_ARGS__) 0 \
344               NRFX_FOR_EACH(_NRFX_MAX_P2, (), __VA_ARGS__))
345 
346 /** @} */
347 
348 #endif /* NRFX_UTILS_H__ */
349