diff options
author | Thomas Haller <thaller@redhat.com> | 2023-10-27 14:28:02 +0200 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2023-11-15 09:32:19 +0100 |
commit | fa500e554058f772de7a5e3faf04886a6c9a4ae5 (patch) | |
tree | d5966b269239b5eff0860aa6b56cad291fc592af | |
parent | 6f4a60b6f2eee9c5c2d983d46b827da38ee2f66d (diff) |
glib-aux: let NM_MIN()/NM_MAX() return a compile time constant
Glib's MIN()/MAX() should not be used, in favor of NM_MIN()/NM_MAX().
That's because the NM variants
- evaluate arguments only once
- have a static assertion that the signedness of the arguments matches
However, previously those macros never evaluated to a compile time
constant. Unlike the glib variants, which do so when the arguments are
compile time constants. That is sometimes important when using the
macros in a context that requires a constant.
Extend NM_MIN()/NM_MAX() to be a compile time constant, when possible.
Note that there are still a few places where NM_MIN()/NM_MAX() cannot be
used due to the expression statement. For those cases, there is
NM_MIN_CONST()/NM_MAX_CONST().
-rw-r--r-- | src/libnm-glib-aux/tests/test-shared-general.c | 15 | ||||
-rw-r--r-- | src/libnm-std-aux/nm-std-aux.h | 63 |
2 files changed, 55 insertions, 23 deletions
diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index 416d26384f..55f8cbb1d1 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -67,6 +67,20 @@ test_nm_static_assert(void) /*****************************************************************************/ static void +test_max(void) +{ + /* Check that NM_MAX() of constant expressions is itself a constant. We + * build with -Wvla, so this is a constant! */ + char buf1[NM_MAX(55, 40)]; + char buf2[NM_MAX(1, NM_MAX(40, 55))]; + + G_STATIC_ASSERT(sizeof(buf1) == 55); + G_STATIC_ASSERT(sizeof(buf2) == 55); +} + +/*****************************************************************************/ + +static void test_gpid(void) { const int *int_ptr; @@ -2624,6 +2638,7 @@ main(int argc, char **argv) nmtst_init(&argc, &argv, TRUE); g_test_add_func("/general/test_nm_static_assert", test_nm_static_assert); + g_test_add_func("/general/test_max", test_max); g_test_add_func("/general/test_gpid", test_gpid); g_test_add_func("/general/test_monotonic_timestamp", test_monotonic_timestamp); g_test_add_func("/general/test_timespect_to", test_timespect_to); diff --git a/src/libnm-std-aux/nm-std-aux.h b/src/libnm-std-aux/nm-std-aux.h index ebc43dd0ab..819f3284cf 100644 --- a/src/libnm-std-aux/nm-std-aux.h +++ b/src/libnm-std-aux/nm-std-aux.h @@ -392,33 +392,50 @@ nm_mult_clamped_u(unsigned a, unsigned b) return c; } -/* glib's MIN()/MAX() macros don't have function-like behavior, in that they evaluate - * the argument possibly twice. - * - * Taken from systemd's MIN()/MAX() macros. */ - -#define NM_MIN(a, b) __NM_MIN(NM_UNIQ, a, NM_UNIQ, b) -#define __NM_MIN(aq, a, bq, b) \ - ({ \ - typeof(a) NM_UNIQ_T(A, aq) = (a); \ - typeof(b) NM_UNIQ_T(B, bq) = (b); \ - \ - NM_STATIC_ASSERT(_NM_INT_SAME_SIGNEDNESS(NM_UNIQ_T(A, aq), NM_UNIQ_T(B, bq))); \ - \ - ((NM_UNIQ_T(A, aq) < NM_UNIQ_T(B, bq)) ? NM_UNIQ_T(A, aq) : NM_UNIQ_T(B, bq)); \ +/* In a few places where a constant expression is required, NM_MIN() doesn't work. + * In that case, use NM_MIN_CONST(). */ +#define NM_MIN_CONST(a, b) \ + ((NM_STATIC_ASSERT_EXPR_1(_NM_INT_SAME_SIGNEDNESS((a), (b)) && __builtin_constant_p((a)) \ + && __builtin_constant_p((b))) \ + && ((a) <= (b))) \ + ? (a) \ + : (b)) + +#define _NM_MIN_V(aq, a, bq, b) \ + ({ \ + typeof(a) NM_UNIQ_T(A, aq) = (a); \ + typeof(b) NM_UNIQ_T(B, bq) = (b); \ + \ + ((NM_UNIQ_T(A, aq) <= NM_UNIQ_T(B, bq)) ? NM_UNIQ_T(A, aq) : NM_UNIQ_T(B, bq)); \ }) -#define NM_MAX(a, b) __NM_MAX(NM_UNIQ, a, NM_UNIQ, b) -#define __NM_MAX(aq, a, bq, b) \ - ({ \ - typeof(a) NM_UNIQ_T(A, aq) = (a); \ - typeof(b) NM_UNIQ_T(B, bq) = (b); \ - \ - NM_STATIC_ASSERT(_NM_INT_SAME_SIGNEDNESS(NM_UNIQ_T(A, aq), NM_UNIQ_T(B, bq))); \ - \ - ((NM_UNIQ_T(A, aq) > NM_UNIQ_T(B, bq)) ? NM_UNIQ_T(A, aq) : NM_UNIQ_T(B, bq)); \ +#define NM_MIN(a, b) \ + __builtin_choose_expr(__builtin_constant_p((a)) && __builtin_constant_p((b)) \ + && NM_STATIC_ASSERT_EXPR_1(_NM_INT_SAME_SIGNEDNESS((a), (b))), \ + (((a) <= (b)) ? (a) : (b)), \ + _NM_MIN_V(NM_UNIQ, a, NM_UNIQ, b)) + +#define NM_MAX_CONST(a, b) \ + ((NM_STATIC_ASSERT_EXPR_1(_NM_INT_SAME_SIGNEDNESS((a), (b)) && __builtin_constant_p((a)) \ + && __builtin_constant_p((b))) \ + && ((a) >= (b))) \ + ? (a) \ + : (b)) + +#define _NM_MAX_V(aq, a, bq, b) \ + ({ \ + typeof(a) NM_UNIQ_T(A, aq) = (a); \ + typeof(b) NM_UNIQ_T(B, bq) = (b); \ + \ + ((NM_UNIQ_T(A, aq) >= NM_UNIQ_T(B, bq)) ? NM_UNIQ_T(A, aq) : NM_UNIQ_T(B, bq)); \ }) +#define NM_MAX(a, b) \ + __builtin_choose_expr(__builtin_constant_p((a)) && __builtin_constant_p((b)) \ + && NM_STATIC_ASSERT_EXPR_1(_NM_INT_SAME_SIGNEDNESS((a), (b))), \ + (((a) >= (b)) ? (a) : (b)), \ + _NM_MAX_V(NM_UNIQ, a, NM_UNIQ, b)) + #define NM_CLAMP(x, low, high) __NM_CLAMP(NM_UNIQ, x, NM_UNIQ, low, NM_UNIQ, high) #define __NM_CLAMP(xq, x, lowq, low, highq, high) \ ({ \ |