From: Linus Torvalds > Sent: 17 November 2024 18:00 > > On Sun, 17 Nov 2024 at 09:42, David Laight <David.Laight@xxxxxxxxxx> wrote: > > > > #define const_true(x) __if_constexpr(x, x, 0) > > No, let's not do this "double expansion" thing again. It would be better that the proposed define :-) > If we actually want to make things smarter, the trick to use is to > know that only a constant _zero_ turns into a void pointer (aka NULL). > > IOW, something like this: > > /* > * iff 'x' is a non-zero constant integer expression, > * then '!(x)' will be a zero constant integer expression, > * and casting that to 'void *' will result in a NULL > * pointer. Otherwise casting it to 'void *' will be just > * a regular 'void *'. > * > * The type of '0 ? NULL : (char *)' is 'char *' > * The type of '0 ? (void *) : (char *) is 'void *' > */ > #define const_true(x) \ > _Generic(0 ? (void *)((long)!(x)) : (char *)0, char *: 1, void *: 0) > > should work, and doesn't do any double expansion of complex arguments. I'm sure I have one place where I did want other than 1 or 0. I do remember moving the '* 0' into the wrapper for __is_constexpr(). Now than min/max don't use __is_constexpr() I wonder if it still has to be sane for pointers? Supporting pointers just makes life hard - especially since (void *)1 isn't constant. I think everything can be built on a base if_const_zero(x, if_z, if_nz) #define const_true(x) if_const_zero(!(x), 1, 0) #define is_constexpr(x) if_const_zero((x) * 0), 1, 0) which gives a bit more flexibility. David - Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)