sys: util: Add clamp macro

Adds CLAMP macro to complement the current min/max macros, as well as a
gcc specific Z_CLAMP macro for single-evaluation expansion.

CLAMP combines the functionality of MIN and MAX, eliminating the
bug-prone usage of MIN(MAX(value, FLOOR), CEIL) found throughout the
codebase in every possible combination.

Signed-off-by: Trond Einar Snekvik <Trond.Einar.Snekvik@nordicsemi.no>
This commit is contained in:
Trond Einar Snekvik 2020-10-27 12:18:44 +01:00 committed by Carles Cufí
commit ed1f75da74
3 changed files with 55 additions and 4 deletions

View file

@ -178,6 +178,16 @@ extern "C" {
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
/**
* @def CLAMP
* @brief Clamp a value to a given range.
* @note Arguments are evaluated multiple times.
*/
#ifndef CLAMP
/* Use Z_CLAMP for a GCC-only, single evaluation version */
#define CLAMP(val, low, high) (((val) <= (low)) ? (low) : MIN(val, high))
#endif
/**
* @brief Is @p x a power of two?
* @param x value to check

View file

@ -474,6 +474,21 @@ do { \
_value_a_ < _value_b_ ? _value_a_ : _value_b_; \
})
/** @brief Return a value clamped to a given range.
*
* Macro ensures that expressions are evaluated only once. See @ref Z_MAX for
* macro limitations.
*/
#define Z_CLAMP(val, low, high) ({ \
/* random suffix to avoid naming conflict */ \
__typeof__(val) _value_val_ = (val); \
__typeof__(low) _value_low_ = (low); \
__typeof__(high) _value_high_ = (high); \
(_value_val_ < _value_low_) ? _value_low_ : \
(_value_val_ > _value_high_) ? _value_high_ : \
_value_val_; \
})
/**
* @brief Calculate power of two ceiling for some nonzero value
*

View file

@ -203,10 +203,10 @@ static int inc_func(void)
return a++;
}
/* Test checks if @ref Z_MAX and @ref Z_MIN return correct result and perform
* single evaluation of input arguments.
/* Test checks if @ref Z_MAX, @ref Z_MIN and @ref Z_CLAMP return correct result
* and perform single evaluation of input arguments.
*/
static void test_z_max_z_min(void)
static void test_z_max_z_min_z_clamp(void)
{
zassert_equal(Z_MAX(inc_func(), 0), 1, "Unexpected macro result");
/* Z_MAX should have call inc_func only once */
@ -215,6 +215,31 @@ static void test_z_max_z_min(void)
zassert_equal(Z_MIN(inc_func(), 2), 2, "Unexpected macro result");
/* Z_MIN should have call inc_func only once */
zassert_equal(inc_func(), 4, "Unexpected return value");
zassert_equal(Z_CLAMP(inc_func(), 1, 3), 3, "Unexpected macro result");
/* Z_CLAMP should have call inc_func only once */
zassert_equal(inc_func(), 6, "Unexpected return value");
zassert_equal(Z_CLAMP(inc_func(), 10, 15), 10,
"Unexpected macro result");
/* Z_CLAMP should have call inc_func only once */
zassert_equal(inc_func(), 8, "Unexpected return value");
}
static void test_CLAMP(void)
{
zassert_equal(CLAMP(5, 3, 7), 5, "Unexpected clamp result");
zassert_equal(CLAMP(3, 3, 7), 3, "Unexpected clamp result");
zassert_equal(CLAMP(7, 3, 7), 7, "Unexpected clamp result");
zassert_equal(CLAMP(1, 3, 7), 3, "Unexpected clamp result");
zassert_equal(CLAMP(8, 3, 7), 7, "Unexpected clamp result");
zassert_equal(CLAMP(-5, -7, -3), -5, "Unexpected clamp result");
zassert_equal(CLAMP(-9, -7, -3), -7, "Unexpected clamp result");
zassert_equal(CLAMP(1, -7, -3), -3, "Unexpected clamp result");
zassert_equal(CLAMP(0xffffffffaULL, 0xffffffff0ULL, 0xfffffffffULL),
0xffffffffaULL, "Unexpected clamp result");
}
static void test_FOR_EACH(void)
@ -433,7 +458,8 @@ void test_cc(void)
ztest_unit_test(test_IF_ENABLED),
ztest_unit_test(test_UTIL_LISTIFY),
ztest_unit_test(test_MACRO_MAP_CAT),
ztest_unit_test(test_z_max_z_min),
ztest_unit_test(test_z_max_z_min_z_clamp),
ztest_unit_test(test_CLAMP),
ztest_unit_test(test_FOR_EACH),
ztest_unit_test(test_FOR_EACH_NONEMPTY_TERM),
ztest_unit_test(test_FOR_EACH_FIXED_ARG),