summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2023-10-27 14:28:02 +0200
committerThomas Haller <thaller@redhat.com>2023-11-15 09:32:19 +0100
commitfa500e554058f772de7a5e3faf04886a6c9a4ae5 (patch)
treed5966b269239b5eff0860aa6b56cad291fc592af
parent6f4a60b6f2eee9c5c2d983d46b827da38ee2f66d (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.c15
-rw-r--r--src/libnm-std-aux/nm-std-aux.h63
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) \
({ \