Skip to content

Commit a1a47e2

Browse files
fsammoura1980kartben
authored andcommitted
util: Add MAX_FROM_LIST macro to find the maximum of variable arguments
This change introduces a set of C preprocessor macros to determine the maximum value from a list of 1 to 10 arguments. The main macro `MAX_FROM_LIST(...)` dispatches to specialized `MAX_N` macros (where N is from 1 to 10) based on the number of arguments provided. This is achieved using a common variadic macro technique involving a helper macro `GET_MAX_MACRO` to count the arguments and select the appropriate implementation. The `MAX_N` macros are defined recursively using the ternary operator `?:` to perform comparisons. This provides a compile-time mechanism to find the maximum value within a small set of numbers. Signed-off-by: Firas Sammoura <[email protected]>
1 parent 6221251 commit a1a47e2

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed

include/zephyr/sys/util.h

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,127 @@ extern "C" {
402402
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
403403
#endif
404404

405+
#ifndef MAX_FROM_LIST
406+
/**
407+
* @brief Returns the maximum of a single value (base case).
408+
* @param a The value.
409+
* @returns The value `a`.
410+
*/
411+
#define Z_MAX_1(a) a
412+
413+
/**
414+
* @brief Returns the maximum of two values.
415+
*
416+
* @note Arguments are evaluated multiple times.
417+
*
418+
* @param a First value.
419+
* @param b Second value.
420+
* @returns Maximum value of @p a and @p b.
421+
*/
422+
#define Z_MAX_2(a, b) ((a) > (b) ? (a) : (b))
423+
424+
/**
425+
* @brief Returns the maximum of three values.
426+
* @note Arguments may be evaluated multiple times.
427+
* @param a First value.
428+
* @param b Second value.
429+
* @param c Third value.
430+
* @returns Maximum value of @p a, @p b, and @p c.
431+
*/
432+
#define Z_MAX_3(a, b, c) Z_MAX_2(a, Z_MAX_2(b, c))
433+
434+
/**
435+
* @brief Returns the maximum of four values.
436+
* @note Arguments may be evaluated multiple times.
437+
* @param a First value.
438+
* @param b Second value.
439+
* @param c Third value.
440+
* @param d Fourth value.
441+
* @returns Maximum value of @p a, @p b, @p c, and @p d.
442+
*/
443+
#define Z_MAX_4(a, b, c, d) Z_MAX_2(Z_MAX_2(a, b), Z_MAX_2(c, d))
444+
445+
/**
446+
* @brief Returns the maximum of five values.
447+
* @note Arguments may be evaluated multiple times.
448+
*/
449+
#define Z_MAX_5(a, b, c, d, e) Z_MAX_2(Z_MAX_4(a, b, c, d), e)
450+
451+
/**
452+
* @brief Returns the maximum of six values.
453+
* @note Arguments may be evaluated multiple times.
454+
*/
455+
#define Z_MAX_6(a, b, c, d, e, f) Z_MAX_2(Z_MAX_5(a, b, c, d, e), f)
456+
457+
/**
458+
* @brief Returns the maximum of seven values.
459+
* @note Arguments may be evaluated multiple times.
460+
*/
461+
#define Z_MAX_7(a, b, c, d, e, f, g) Z_MAX_2(Z_MAX_6(a, b, c, d, e, f), g)
462+
463+
/**
464+
* @brief Returns the maximum of eight values.
465+
* @note Arguments may be evaluated multiple times.
466+
*/
467+
#define Z_MAX_8(a, b, c, d, e, f, g, h) Z_MAX_2(Z_MAX_7(a, b, c, d, e, f, g), h)
468+
469+
/**
470+
* @brief Returns the maximum of nine values.
471+
* @note Arguments may be evaluated multiple times.
472+
*/
473+
#define Z_MAX_9(a, b, c, d, e, f, g, h, i) Z_MAX_2(Z_MAX_8(a, b, c, d, e, f, g, h), i)
474+
475+
/**
476+
* @brief Returns the maximum of ten values.
477+
* @note Arguments may be evaluated multiple times.
478+
*/
479+
#define Z_MAX_10(a, b, c, d, e, f, g, h, i, j) Z_MAX_2(Z_MAX_9(a, b, c, d, e, f, g, h, i), j)
480+
481+
/**
482+
* @brief Helper macro to select the correct MAX_N macro.
483+
*
484+
* This macro uses the argument-counting trick to pick the correct
485+
* `Z_MAX_N` macro name from the arguments provided to `MAX_FROM_LIST`.
486+
* The 10th argument (or 11th including `NAME`) effectively becomes the
487+
* macro name to use.
488+
*
489+
* @param _1 Positional argument 1.
490+
* @param _2 Positional argument 2.
491+
* @param _3 Positional argument 3.
492+
* @param _4 Positional argument 4.
493+
* @param _5 Positional argument 5.
494+
* @param _6 Positional argument 6.
495+
* @param _7 Positional argument 7.
496+
* @param _8 Positional argument 8.
497+
* @param _9 Positional argument 9.
498+
* @param _10 Positional argument 10.
499+
* @param NAME The macro name to be selected.
500+
* @param ... Additional arguments.
501+
* @returns The selected macro name `NAME`.
502+
*/
503+
#define Z_GET_MAX_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, NAME, ...) NAME
504+
505+
/**
506+
* @brief Finds the maximum value from a list of 1 to 10 arguments.
507+
*
508+
* Dispatches to the appropriate internal `Z_MAX_N` macro based on the number of
509+
* arguments provided.
510+
*
511+
* Example Usage:
512+
* MAX_FROM_LIST(1, 5, 2)
513+
* MAX_FROM_LIST(10)
514+
*
515+
* @note Arguments may be evaluated multiple times by the underlying
516+
* `Z_MAX_N` macros. Avoid expressions with side effects.
517+
*
518+
* @param ... A list of 1 to 10 values to compare.
519+
* @returns The maximum value among the arguments.
520+
*/
521+
#define MAX_FROM_LIST(...) \
522+
Z_GET_MAX_MACRO(__VA_ARGS__, Z_MAX_10, Z_MAX_9, Z_MAX_8, Z_MAX_7, Z_MAX_6, Z_MAX_5, \
523+
Z_MAX_4, Z_MAX_3, Z_MAX_2, Z_MAX_1)(__VA_ARGS__)
524+
#endif
525+
405526
#ifndef CLAMP
406527
/**
407528
* @brief Clamp a value to a given range.

tests/unit/util/main.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,44 @@ ZTEST(util, test_z_max_z_min_z_clamp) {
267267
zassert_equal(inc_func(false), 8, "Unexpected return value");
268268
}
269269

270+
ZTEST(util, test_max_from_list_macro) {
271+
/* Test with one argument */
272+
zassert_equal(MAX_FROM_LIST(10), 10, "Should return the single value.");
273+
274+
/* Test with two arguments */
275+
zassert_equal(MAX_FROM_LIST(10, 20), 20, "Should return 20.");
276+
zassert_equal(MAX_FROM_LIST(30, 15), 30, "Should return 30.");
277+
278+
/* Test with three arguments */
279+
zassert_equal(MAX_FROM_LIST(10, 5, 20), 20, "Should return 20.");
280+
zassert_equal(MAX_FROM_LIST(30, 15, 25), 30, "Should return 30.");
281+
zassert_equal(MAX_FROM_LIST(5, 40, 35), 40, "Should return 40.");
282+
283+
/* Test with five arguments */
284+
zassert_equal(MAX_FROM_LIST(10, 50, 20, 5, 30), 50, "Should return 50.");
285+
286+
/* Test with seven arguments */
287+
zassert_equal(MAX_FROM_LIST(10, 50, 20, 5, 30, 45, 25), 50, "Should return 50.");
288+
289+
/* Test with eight arguments */
290+
zassert_equal(MAX_FROM_LIST(1, 2, 3, 4, 5, 6, 7, 8), 8, "Should return 8.");
291+
zassert_equal(MAX_FROM_LIST(10, 5, 20, 15, 30, 25, 35, 40), 40, "Should return 40.");
292+
293+
/* Test with nine arguments */
294+
zassert_equal(MAX_FROM_LIST(1, 2, 3, 4, 5, 6, 7, 8, 9), 9, "Should return 9.");
295+
zassert_equal(MAX_FROM_LIST(10, 5, 20, 15, 30, 25, 35, 40, 45), 45, "Should return 45.");
296+
297+
/* Test with ten arguments */
298+
zassert_equal(MAX_FROM_LIST(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 10, "Should return 10.");
299+
zassert_equal(MAX_FROM_LIST(10, 9, 8, 7, 6, 5, 4, 3, 2, 1), 10, "Should return 10.");
300+
zassert_equal(MAX_FROM_LIST(5, 15, 25, 35, 45, 55, 65, 75, 85, 95),
301+
95, "Should return 95.");
302+
303+
/* Test with various values */
304+
zassert_equal(MAX_FROM_LIST(25600, 12800, 9800), 25600, "Should return 25600.");
305+
zassert_equal(MAX_FROM_LIST(9800, 25600, 12800), 25600, "Should return 25600.");
306+
}
307+
270308
ZTEST(util, test_CLAMP) {
271309
zassert_equal(CLAMP(5, 3, 7), 5, "Unexpected clamp result");
272310
zassert_equal(CLAMP(3, 3, 7), 3, "Unexpected clamp result");

0 commit comments

Comments
 (0)