Je n'ai pas de solution pour sizeof(void)
n'étant pas standard, mais vous pourriez contourner la possibilité que sizeof(void) == sizeof(int)
en faisant quelque chose comme :
#define ICE_P(x) ( \
sizeof(void) != \
sizeof(*( \
1 ? \
((void*) ((x) * 0L) ) : \
((struct { char v[sizeof(void) * 2]; } *) 1) \
) \
) \
)
Je sais que ce n'est pas une réponse complète, mais c'est légèrement plus près
Editar : J'ai fait un peu de recherche sur les solutions qui fonctionnent sur les différents compilateurs. J'ai encodé toutes les informations suivantes dans le fichier Hedley ; voir le HEDLEY_IS_CONSTANT
, HEDLEY_REQUIRE_CONTEXPR
y HEDLEY__IS_CONSTEXPR
macros. Il est du domaine public et ne comporte qu'un seul en-tête, ce qui permet de l'intégrer facilement à votre projet, ou de copier les parties qui vous intéressent.
C11 Macro & Variantes
macro C11 de l'utilisateur2357112 devrait travailler sur cualquier compilateur C11, mais SunCC y IGP sont actuellement cassés, vous devrez donc les mettre sur liste noire. De plus, IAR définit __STDC_VERSION__
en mode C++, et cette astuce ne fonctionne pas en C++ (AFAIK rien ne va ), donc vous voudrez probablement vous assurer que __cplusplus
n'est pas défini. J'ai vérifié qu'il fonctionne réellement sur GCC, clang (et les compilateurs dérivés de clang comme emscripten), ICC, IAR, et XL C/C++.
En dehors de cela, certains compilateurs supportent _Generic
même dans les anciens modes comme une extension :
- GCC 4.9+.
- clang ; vérifier avec
__has_feature(c_generic_selections)
(vous pouvez désactiver l'option -Wc11-extensions
avertissement, cependant)
- ICC 16.0+.
- XL C/C++ 12.1+
Notez également que, parfois, les compilateurs émettent un avertissement lorsque vous intégrez un fichier int
à un void*
vous pouvez contourner ce problème en effectuant d'abord un casting vers un intptr_t
puis a void*
:
#define ICE_P(expr) _Generic((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)
Ou, pour les compilateurs (tels que GCC) qui définissent __INTPTR_TYPE__
vous pouvez l'utiliser à la place de intptr_t
et vous n'avez pas besoin d'inclure stdint.h
.
Une autre mise en œuvre possible ici est d'utiliser __builtin_types_compatible_p
au lieu de _Generic
. Je ne connais pas de compilateurs où cela fonctionnerait alors que la macro originale ne le ferait pas, mais cela vous permet de vous sortir d'une situation de -Wpointer-arith
avertissement :
#define IS_CONSTEXPR(expr) \
__builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)
Cette version devrait fonctionner avec GCC jusqu'à la version 3.1, ainsi qu'avec les compilateurs qui définissent l'option __GNUC__
/ __GNUC_MINOR__
à des valeurs qui indiquent 3.1 comme clang et ICC.
Macro de cette réponse
Tout compilateur qui supporte sizeof(void)
devrait fonctionner, mais il y a de fortes chances que vous rencontriez un avertissement (tel que -Wpointer-arith
). Ceci étant dit, les compilateurs AFAICT qui hacer soutien sizeof(void)
semblent l'avoir toujours fait, donc cualquier de ces compilateurs devrait fonctionner :
- CCG
- Clang (et les compilateurs construits en dans, qui définissent aussi
__clang__
)
- ICC (testé 18.0)
- XL C/C++ (testé en 13.1.6)
- TI (testé 8.0)
- TinyCC
__builtin_constant_p
En fonction de votre cas d'utilisation, il peut être préférable d'utiliser l'option __builtin_constant_p
sur les compilateurs qui le supportent. C'est un peu plus général (et plus nébuleux) qu'une expression de constante entière ; cela signifie simplement que le compilateur connaît la valeur au moment de la compilation. Ces compilateurs sont connus pour la supporter :
- GCC 3.1+.
- Clang
- ICC (testé 18.0)
- TinyCC 0.9.19+
- armcc 5.04+
- XL C/C++ (non documenté, mais il fonctionne certainement en 13.1.6+)
Si vous utilisez la macro pour choisir entre un chemin de code que le compilateur peut plier en permanence s'il connaît la valeur au moment de la compilation, mais qui est lent à l'exécution, et un chemin de code qui est une boîte noire pour le compilateur mais qui est rapide à l'exécution, utilisez __builtin_constant_p
.
Par contre, si vous voulez vérifier que la valeur est bien un ICE selon la norme, n'utilisez pas __builtin_constant_p
. À titre d'exemple, voici une macro qui retournera expr
si le expr
est un ICE, mais -1 s'il ne l'est pas :
#if defined(ICE_P)
# define REQUIRE_ICE(expr) (ICE_P(expr) ? (expr) : (-1))
#else
# define REQUIRE_ICE(expr) (expr)
#endif
Vous pouvez ensuite l'utiliser lorsque vous déclarez un tableau dans une macro si vous voulez que le compilateur affiche une erreur si vous utilisez un VLA :
char foo[REQUIRE_ICE(bar)];
Cela dit, GCC et clang implémentent tous les deux une fonction -Wvla
que vous pouvez utiliser à la place. L'avantage de -Wvla
est qu'elle ne nécessite pas de modification du code source (c'est-à-dire que vous pouvez simplement écrire char foo[bar];
). Les inconvénients sont qu'il n'est pas aussi largement supporté, et que l'utilisation de paramètres de tableau conformes déclenchera également le diagnostic, donc si vous voulez éviter un grand nombre de faux positifs, cette macro peut être votre meilleure option.
Compilateurs qui ne supportent rien
Les idées sont les bienvenues :)
0 votes
Vous n'avez pas montré l'utilisation de la macro. Veuillez afficher la Exemple minimal, complet et vérifiable qui montre le problème.
22 votes
@WeatherVane Ce n'est pas une question de débogage ("pourquoi ce code ne fonctionne-t-il pas ?").
0 votes
@melpomene pourquoi cette exemption ? J'aimerais voir comment cela est utilisé.
4 votes
@WeatherVane Voir votre lien : " Lorsque vous posez une question sur un problème causé par votre code ... " Elle ne s'applique pas ici.
0 votes
@melpomene J'éviterais les approches "intelligentes" et j'aimerais quand même voir un peu de contexte.
7 votes
@WeatherVane Il y a un exemple à lkml.org/lkml/2018/3/21/223 ; en gros, l'objectif est de définir, par exemple, une
MAX
une macro qui est à la fois sûre avec des arguments efficaces et qui est une expression constante avec des arguments constants. L'utilisation d'une fonction en ligne ou de variables temporaires remplit la condition #1 mais pas la condition #2 ; l'utilisation de?:
et le fait de répéter les expressions de l'argument répond au point 2 mais pas au point 1. Si vous pouviez détecter les expressions constantes, vous pourriez avoir le beurre et l'argent du beurre.0 votes
Il y a quelque chose à propos
char *
yvoid *
ayant des représentations identiques/compatibles, alors peut-être utiliser(char*)(void*)((x)
pour évitersizeof(void)
.0 votes
@chux qui ne compilerait pas, car l'opérateur conditionnel nécessite qu'un des opérandes soit implicitement convertible en type de l'autre.