2016-07-23 5 views
1

C99 permet initializers de matrice (entre autres) pour spécifier quel élément de la matrice est en cours réglé avec un indicateur de nombre entier positif (6.7.8.6 $, 6.7.8.17 $), par exemple comme ceci:codes dans c89

const char *foo[] = {[2] = "foo", [1] = "bar", [0] = "baz"}; 

Je l'ai déjà utilisé cela pour faire une table ENUM à cordes comme ceci:

enum {THING_FOO = 0, THING_BAR, THING_BAZ}; 
const char *table[] = { 
    [THING_FOO] = "foo", 
    [THING_BAR] = "bar", 
    [THING_BAZ] = "baz" 
} 

Cependant, je travaille maintenant sous la condition que mon code est conforme à C89. J'ai examiné la magie du préprocesseur (comme dans here, par exemple) mais j'ai besoin que les chaînes soient arbitraires, pas les copies des symboles enum.

Il ne suffit pas de faire juste

enum {THING_FOO = 0, THING_BAR, THING_BAZ}; 
const char *table[] = {"foo", "bar", "baz"}; 

parce que je vais devoir ajouter des éléments de ENUM à l'avenir. En utilisant la méthode c99, cela se traduirait par des pointeurs NULL dans la table qui sont facilement acceptables pour le débogage s'ils deviennent des problèmes. Si j'ai oublié de mettre à jour la table de chaînes en utilisant cette méthode, j'obtiendrais des segfaults qui sont plus difficiles à déboguer. De plus, il est inutile d'avoir des symboles si je dois me souvenir des décalages de toute façon.

Si la déclaration était en fonction, je pourrais obtenir l'effet désiré comme ceci:

enum {THING_FOO = 0, THING_BAR, THING_BAZ, NUM_THINGS}; 
void foo(void) 
{ 
    static const char *table[NUM_THINGS]; 
    table[THING_FOO] = "foo"; 
    table[THING_BAR] = "bar"; 
    table[THING_BAZ] = "baz"; 

    /* ... */ 
} 

Cependant, au moins avec gcc, cela ne pas obtenir optimisé.

Existe-t-il un moyen de déclarer une telle table de chaînes dans c89? (Il n'y a pas de problème dans l'assemblage.)

+0

Possible copie de [Comment convertir les noms d'enum en chaîne en c] (http://stackoverflow.com/questions/9907160/how-to-convert-enum-names-to-string-in-c) – Leandros

+1

De nombreuses façons, choisissez la façon dont vous pensez que c'est la manière la moins laide. Il y a beaucoup, beaucoup de façons, l'une pire que l'autre. – Leandros

+0

À votre question, rien d'aussi élégant que C99 ou plus tard ne laissera les lacunes que vous exploitez entre les valeurs enum. Je suis un peu confus, cependant. Puisque vous avez clairement besoin de changer le code de toute façon, lequel, selon vous, prendrait * moins * de temps: une "énorme déclaration de changement", ou postez-vous cette question, et * encore * vous devez changer le code partout? – WhozCraig

Répondre

0

Après avoir essayé quelques techniques différentes, celui-ci est plus facile à maintenir:

const char *table[] = { 
#define FOO 0 
    "foo", 
#define BAR (FOO + 1) 
    "bar", 
#define BAZ (BAR + 1) 
    "baz" 
} 

Ici toutes les informations au sujet d'une entrée est en cluster. Pour insérer un élément, vous n'avez qu'à modifier les choses tout autour. Par exemple, pour insérer qux:

const char *table[] = { 
#define FOO 0 
    "foo", 
#define QUX (FOO + 1) /* new */ 
    "qux",    /* new */ 
#define BAR (QUX + 1) /* modified */ 
    "bar", 
#define BAZ (BAR + 1) 
    "baz" 
} 

Il est un peu laid (genre de attachante, vous savez?), Mais il fonctionne bien.

1

Qu'en est simple, ancienne

const char* table[] = { "foo", "bar", "baz" }; 

En d'autres termes, il suffit de les mettre dans le bon ordre.

char *foo_string = table[FOO]; 

Bien sûr, cela ne fonctionne que pour les énumérations simples comme ci-dessus, pas pour les énumérations dans le style

enum { FOO = 13; BAR = 15, BAZ = 312 }; 

Mais pour cela, vous devez créer un tableau avec au moins 313 éléments, la plupart sont NULL de toute façon, ce qui serait une construction assez inutile. Dans de tels cas, le compilateur peut optimiser cela pour vous, lorsque vous utilisez une construction switch.


Regardez également le S.O. question @Leandros pointé à: How to convert enum names to string in c. La réponse utilise des macros pour générer le tableau, ce qui garantit que les entrées sont dans le bon ordre.

Ou, comme cette réponse dit:

#define enum_str(s) #s 

qui se débarrasse du tableau tout à fait.

+0

Le problème avec l'autre réponse est qu'il ne permet pas d'assigner une chaîne arbitraire à chaque élément enum; Je vais clarifier dans la question. – nebuch

+0

Ok, vous obtenez seulement les noms dans le code source. Donc FOO n'est pas lié à "foo", il est automatiquement "FOO". Ensuite, les tableaux sont une solution. Ou, disons, des tables de hachage. Mais ces derniers ne sont plus si simples. Ensuite, une construction de commutateur est probablement plus facile, d'autant plus que les enums ont rarement des centaines d'éléments ou plus. –

+0

Je viens de vous voir avoir ces énormes enums. Ensuite, les choses deviennent soit compliquées ou difficiles à gérer. Pourquoi ces enums sont-ils si grands? –

2
#define DEF_FOO_ENUM(E0, S0, E1, S1, E2, S2) \ 
    enum foo    { E0, E1, E2 }; \ 
    const char *foo_str = { S0, S1, S2 }; 

DEF_FOO_ENUM(THING_FOO, "foo", 
       THING_BAR, "bar", 
       THING_BAZ, "baz"); 

Les symboles et les chaînes sont appariés.Vous n'allez pas facilement ajouter un nouveau symbole sans chaîne, ou vice versa. Pour ajouter un élément, vous devez ajouter deux nouveaux arguments à la macro — E3, S3 — et ainsi de suite. Il n'y a rien à synchroniser là-bas, juste que le enum a tous les E -s et le tableau a tous les S -s. C'est presque impossible à bousiller.

+0

Certaines des tables contiennent plus de 100 éléments. J'aime cette solution, mais cela fonctionnera pour beaucoup d'Es et de Ss. – nebuch

+3

Je ferais soit la génération de code, soit la validation avec un outil externe. C'est-à-dire, un utilitaire de traitement de texte qui comprend spécifiquement ces déclarations (qui sont main-maintenu) et vérifie leur intégrité. – Kaz

1

Vous pouvez les garder ensemble en utilisant X-macros:

#define MYXMACRO(OP) \ 
    OP(ENUM_FOO, "foo") \ 
    OP(ENUM_BAR, " bar") \ 
    OP(ENUM_BAZ, "baz") 

/* use the first parameter to set up your enums*/ 
enum { 
#define AS_ENUMS(x,y) x, 
MYXMACRO(AS_ENUMS) 
#undef AS_ENUMS /*not required, just playing nice*/ 
NUMTHINGS 
}; 

/* use the 2nd parameter to set up your strings*/ 
const char *strings[] = { 
#define AS_STRINGS(x,y) y, 
MYXMACRO(AS_STRINGS) 
#undef AS_STRINGS 
}; 
#undef MYXMACRO 

Maintenant vos nouvelles données peuvent être ajoutées comme un ensemble de ENUM et la chaîne. Si vous avez ensuite décidé d'ajouter quelque chose d'autre en fonction des énumérations, il est facilement étendu avec un paramètre 'z' à OP() ou même ... et __VA_ARGS__ pour un nombre de paramètres multiple mais variable.