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().
This commit is contained in:
Thomas Haller 2023-10-27 14:28:02 +02:00
parent 6f4a60b6f2
commit fa500e5540
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
2 changed files with 54 additions and 22 deletions

View file

@ -66,6 +66,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)
{
@ -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);

View file

@ -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. */
/* 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(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)); \
#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) \
({ \