I don't have a fix for sizeof(void)
not being standard, but you could work around the possibility that sizeof(void) == sizeof(int)
by doing something like:
#define ICE_P(x) ( \
sizeof(void) != \
sizeof(*( \
1 ? \
((void*) ((x) * 0L) ) : \
((struct { char v[sizeof(void) * 2]; } *) 1) \
) \
) \
)
I know it's not a full answer, but it's slightly closer…
Edit: I've done a bit of research about which solutions work on various compilers. I've encoded all of the following information in Hedley; see the HEDLEY_IS_CONSTANT
, HEDLEY_REQUIRE_CONTEXPR
, and HEDLEY__IS_CONSTEXPR
macros. It's public domain, and a single header, so it's very easy to just drop into your project, or you can copy the bits you're interested in.
C11 Macro & Variants
user2357112's C11 macro should work on any C11 compiler, but SunCC and PGI are currently broken so you'll have to blacklist them. Also, IAR defines __STDC_VERSION__
in C++ mode, and this trick doesn't work in C++ (AFAIK nothing does), so you'll probably want to make sure __cplusplus
isn't defined. I've verified that it really does work on GCC, clang (and clang-derived compilers like emscripten), ICC, IAR, and XL C/C++.
Other than that, some compilers support _Generic
even in older modes as an extension:
- GCC 4.9+
- clang; check with
__has_feature(c_generic_selections)
(you may want to disable the -Wc11-extensions
warning, though)
- ICC 16.0+
- XL C/C++ 12.1+
Also, note that sometimes compilers emit a warning when you cast an int
to a void*
; you can get around this by first casting to an intptr_t
then to void*
:
#define ICE_P(expr) _Generic((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)
Or, for compilers (such as GCC) which define __INTPTR_TYPE__
, you can use that instead of intptr_t
and you needn't include stdint.h
.
Another possible implementation here is to use __builtin_types_compatible_p
instead of _Generic
. I'm not aware of any compilers where that would work but the original macro wouldn't, but it does get you out of a -Wpointer-arith
warning:
#define IS_CONSTEXPR(expr) \
__builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)
This version should work with GCC back to 3.1, as well as compilers which define __GNUC__
/__GNUC_MINOR__
to values which indicate ≥ 3.1 such as clang and ICC.
Macro from this answer
Any compiler which supports sizeof(void)
should work, but there is a good chance you'll run into a warning (such as -Wpointer-arith
). That said, AFAICT compilers which do support sizeof(void)
seem to have always done so, so any version of these compilers should work:
- GCC
- Clang (and compilers built in in, which also define
__clang__
)
- ICC (tested 18.0)
- XL C/C++ (tested 13.1.6)
- TI (tested 8.0)
- TinyCC
__builtin_constant_p
Depending on your use case, it may be preferable to use __builtin_constant_p
on compilers which support it. It's a bit more general (and more nebulous) than an integer constant expression; it just says that the compiler knows the value at compile-time. These compilers are known to support it:
- GCC 3.1+
- Clang
- ICC (tested 18.0)
- TinyCC 0.9.19+
- armcc 5.04+
- XL C/C++ (undocumented, but it definitely works in 13.1.6+)
If you're using the macro to choose between a code path which the compiler can constant fold if it knows the value at compile time but is slow at runtime and a code path which is a black box to the compiler but is fast at runtime, use __builtin_constant_p
.
OTOH, if you want to check to make sure the value is really an ICE according to the standard, don't use __builtin_constant_p
. As an example, here is a macro which will return expr
if the expr
is an ICE, but -1 if it isn't:
#if defined(ICE_P)
# define REQUIRE_ICE(expr) (ICE_P(expr) ? (expr) : (-1))
#else
# define REQUIRE_ICE(expr) (expr)
#endif
You can then use that when declaring an array in a macro if you the compiler to show an error if you use a VLA:
char foo[REQUIRE_ICE(bar)];
That said, GCC and clang both implement a -Wvla
warning which you may want to use instead. The advantage of -Wvla
is that it doesn't require source code modifications (i.e., you can just write char foo[bar];
). The disadvantages are that it isn't as widely supported, and that using conformant array parameters will also trigger the diagnostic, so if you want to avoid lots of false positives this macro may be your best bet.
Compilers which don't support anything
Ideas welcome :)