Enum ne devrait-il jamais être utilisé dans une API?

J’utilise une bibliothèque C fournie à moi déjà compilé. J’ai peu d’informations sur le compilateur, la version, les options, etc., utilisées lors de la compilation de la bibliothèque. L’interface de la bibliothèque utilise enum fois dans les structures transmises et directement en tant que parameters passés.

La question qui se pose est la suivante: comment puis-je assurer ou établir que, lorsque je comstack du code pour utiliser la bibliothèque fournie, mon compilateur utilisera la même taille pour ces enum ? Si ce n’est pas le cas, les structures ne s’aligneront pas et le paramètre qui passe peut être perturbé, par exemple long vs. int .

Ma préoccupation découle de la norme C99, qui stipule que le type d’ enum :

doit être compatible avec char, un type entier signé ou un type entier non signé. Le choix du type est défini par l’implémentation, mais doit pouvoir représenter les valeurs de tous les membres de l’énumération.

Autant que je sache, tant que la valeur la plus grande convient, le compilateur peut choisir n’importe quel type qui lui plait le plus, de manière efficace, sur un coup de tête, en variant potentiellement non seulement entre les compilateurs, mais également avec des versions différentes du même compilateur et / ou des options du compilateur. . Il pourrait choisir des représentations sur 1, 2, 4 ou 8 octets, ce qui entraînerait des incompatibilités potentielles dans les structures et le passage de parameters. (Cela pourrait également permettre de choisir signé ou non signé, mais je ne vois pas de mécanisme pour que ce soit un problème dans ce contexte.)

Est-ce que j’ai râté quelque chose? Si je ne manque pas quelque chose, cela signifie-t-il que l’ enum ne doit jamais être utilisé dans une API?

Mettre à jour:

Oui, il me manquait quelque chose. Bien que la spécification de la langue ne soit d’aucune utilité ici, comme l’a noté @Barmar, l’interface binary d’application (ABI) le permet. Sinon, l’ABI est déficient. L’ ABI de mon système spécifie en effet qu’un enum doit être un entier signé sur quatre octets. Si un compilateur n’obéit pas à cela, c’est un bogue. Avec une ABI complète et des compilateurs conformes, enum peut être utilisé en toute sécurité dans une API.

Les API qui utilisent enum dépendent de l’hypothèse que le compilateur sera cohérent, c’est-à-dire que, avec la même déclaration enum, il choisira toujours le même type sous-jacent.

Bien que la norme de langage n’exige pas spécifiquement cela, il serait assez pervers qu’un compilateur fasse autre chose.

En outre, tous les compilateurs pour un système d’exploitation particulier doivent être compatibles avec le ABI du système d’exploitation. Sinon, vous auriez beaucoup plus de problèmes, tels que la bibliothèque utilisant un int 64 bits alors que l’appelant utilisait un de 32 bits. Idéalement, l’ABI devrait limiter la représentation des enum pour assurer la compatibilité.

Plus généralement, la spécification de langage assure uniquement la compatibilité entre les programmes compilés avec la même implémentation. L’ABI assure la compatibilité entre les programmes compilés avec différentes implémentations.

De la question:

L’ABI de mon système spécifie en effet qu’un enum doit être un entier signé sur quatre octets. Si un compilateur n’obéit pas à cela, c’est un bogue.

Je suis surpris à ce sujet. Je suppose qu’en réalité, votre compilateur sélectionnera une taille de 64 bits (8 octets) pour votre enum si vous définissez une constante énumérée avec une valeur supérieure à 2 ^ 32.

Sur mes plates-formes (MinGW gcc 4.6.2 ciblant x86 et gcc 4, .4 sous Linux ciblant x86_64), le code suivant indique que je reçois des énums de 4 et 8 octets:

 #include  enum { a } foo; enum { b = 0x123456789 } bar; int main(void) { printf("%lu\n", sizeof(foo)); printf("%lu", sizeof(bar)); return 0; } 

J’ai compilé avec les commutateurs -Wall -std=c99 .

Je suppose que vous pourriez dire qu’il s’agit d’un bogue du compilateur. Toutefois, les alternatives consistant à supprimer le support des constantes énumérées supérieures à 2 ^ 32 ou à toujours utiliser des énumérations de 8 octets semblent indésirables.

Étant donné que ces versions courantes de GCC ne fournissent pas une énumération de taille fixe, je pense que la seule action sûre en général consiste à ne pas utiliser d’énumérations dans les API.

Notes complémentaires pour GCC

La compilation avec “-pedantic” entraîne la génération des avertissements suivants:

 main.c:4:8: warning: integer constant is too large for 'long' type [-Wlong-long] main.c:4:12: warning: ISO C ressortingcts enumerator values to range of 'int' [-pedantic] 

Le comportement peut être personnalisé via les commutateurs –short-enums et –no-short-enums.

Résultats avec Visual Studio

La compilation du code ci-dessus avec VS 2008 x86 entraîne les avertissements suivants:

 warning C4341: 'b' : signed value is out of range for enum constant warning C4309: 'initializing' : truncation of constant value 

Et avec VS 2013 x86 et x64, il suffit de:

 warning C4309: 'initializing' : truncation of constant value