Comment faire printf pour afficher les valeurs des variables qui sont de type enum? Par exemple:
typedef enum {Linux, Apple, Windows} OS_type;
OS_type myOS = Linux;
et ce dont j'ai besoin est quelque chose comme
printenum(OS_type, "My OS is %s", myOS);
qui doit afficher une chaîne "Linux", pas un entier.
Je suppose que je dois d'abord créer un tableau de chaînes indexées sur la valeur. Mais je ne sais pas si c'est la plus belle façon de le faire. Est-ce possible du tout?
c++
c
preprocessor
ansi-c
psihodelia
la source
la source
Réponses:
Il n'y a vraiment pas de belle façon de faire cela. Configurez simplement un tableau de chaînes indexées par l'énumération.
Si vous faites beaucoup de sortie, vous pouvez définir un opérateur << qui prend un paramètre d'énumération et effectue la recherche à votre place.
la source
La solution naïve, bien sûr, est d'écrire une fonction pour chaque énumération qui effectue la conversion en chaîne:
Ceci, cependant, est un désastre de maintenance. Avec l'aide de la bibliothèque Boost.Preprocessor, qui peut être utilisée avec du code C et C ++, vous pouvez facilement tirer parti du préprocesseur et le laisser générer cette fonction pour vous. La macro de génération est la suivante:
La première macro (commençant par
X_
) est utilisée en interne par la seconde. La deuxième macro génère d'abord l'énumération, puis génère uneToString
fonction qui prend un objet de ce type et renvoie le nom de l'énumérateur sous forme de chaîne (cette implémentation, pour des raisons évidentes, nécessite que les énumérateurs mappent sur des valeurs uniques).En C ++, vous pouvez implémenter la
ToString
fonction comme uneoperator<<
surcharge à la place, mais je pense que c'est un peu plus simple d'exiger un "ToString
" explicite pour convertir la valeur en chaîne.À titre d'exemple d'utilisation, votre
OS_type
énumération serait définie comme suit:Alors que la macro semble au premier abord être beaucoup de travail et que la définition de
OS_type
semble plutôt étrangère, rappelez-vous que vous devez écrire la macro une fois, puis vous pouvez l'utiliser pour chaque énumération. Vous pouvez y ajouter des fonctionnalités supplémentaires (par exemple, une conversion sous forme de chaîne en enum) sans trop de problèmes, et cela résout complètement le problème de maintenance, puisque vous ne devez fournir les noms qu'une seule fois, lorsque vous appelez la macro.L'énumération peut alors être utilisée comme si elle était définie normalement:
Les extraits de code de cet article, en commençant par la
#include <boost/preprocessor.hpp>
ligne, peuvent être compilés tels que publiés pour illustrer la solution.Cette solution particulière est pour C ++ car elle utilise une syntaxe spécifique à C ++ (par exemple, non
typedef enum
) et une surcharge de fonctions, mais il serait simple de faire fonctionner cela aussi avec C.la source
(Windows)
en(Windows, 3)
puis remplacer leBOOST_PP_SEQ_ENUM
par unBOOST_PP_SEQ_FOR_EACH
. Je n'ai pas d'exemple à portée de main, mais je peux en écrire un si vous le souhaitez.Ceci est le bloc pré-processeur
Définition enum
Appeler en utilisant
Pris d' ici . À quel point cela est cool ? :)
la source
Utilisez
std::map<OS_type, std::string>
et remplissez-le avec enum comme clé et une représentation sous forme de chaîne comme valeurs, vous pouvez alors faire ceci:la source
Le problème avec les énumérations C est que ce n'est pas un type qui lui est propre, comme c'est le cas en C ++. Une énumération en C est un moyen de mapper des identificateurs à des valeurs intégrales. Juste ça. C'est pourquoi une valeur enum est interchangeable avec des valeurs entières.
Comme vous le devinez correctement, un bon moyen est de créer un mappage entre la valeur enum et une chaîne. Par exemple:
la source
enum
sont des types en C. Les constantes de type énumération intégrale sont de typeint
et non duenum
type par lequel elles sont définies, c'est peut-être ce que vous vouliez dire. Mais je ne vois pas du tout ce que cela a à voir avec la question.J'ai combiné les solutions de James , Howard et Éder et créé une implémentation plus générique:
Le code complet est écrit ci-dessous (utilisez "DEFINE_ENUM_CLASS_WITH_ToString_METHOD" pour définir une énumération) ( démo en ligne ).
la source
Cet exemple simple a fonctionné pour moi. J'espère que cela t'aides.
la source
Avez-vous essayé ceci:
La
stringify()
macro peut être utilisée pour transformer n'importe quel texte de votre code en chaîne, mais uniquement le texte exact entre les parenthèses. Il n'y a pas de déréférencement de variables ou de substitutions de macros ou toute autre sorte de chose faite.http://www.cplusplus.com/forum/general/2949/
la source
Il y a beaucoup de bonnes réponses ici, mais j'ai pensé que certaines personnes pourraient trouver la mienne utile. Je l'aime parce que l'interface que vous utilisez pour définir la macro est à peu près aussi simple que possible. C'est aussi pratique car vous n'avez pas besoin d'inclure de bibliothèques supplémentaires - tout est livré avec C ++ et il ne nécessite même pas une version très tardive. J'ai extrait des articles de divers endroits en ligne, donc je ne peux pas m'en attribuer le mérite, mais je pense que c'est assez unique pour justifier une nouvelle réponse.
Commencez par créer un fichier d'en-tête ... appelez-le EnumMacros.h ou quelque chose comme ça, et mettez-y ceci:
Ensuite, dans votre programme principal, vous pouvez le faire ...
Où serait la sortie >> La valeur de 'Apple' est: 2 sur 4
Prendre plaisir!
la source
En supposant que votre énumération est déjà définie, vous pouvez créer un tableau de paires:
Maintenant, vous pouvez créer une carte:
Maintenant, vous pouvez utiliser la carte. Si votre énumération est modifiée, vous devez ajouter / supprimer une paire des paires de tableaux []. Je pense que c'est le moyen le plus élégant d'obtenir une chaîne d'énumération en C ++.
la source
bimap
au cas où l'on voudrait analyser les noms et les transformer en énumérations (par exemple, à partir d'un fichier XML).Pour C99, il y a
P99_DECLARE_ENUM
dans P99 qui vous permet simplement de déclarerenum
comme ceci:puis utilisez
color_getname(A)
pour obtenir une chaîne avec le nom de la couleur.la source
Voici mon code C ++:
la source
Un peu tard à la fête, mais voici ma solution C ++ 11:
la source
error: ‘hash’ is not a class template
->#include <functional>
Ma propre préférence est de minimiser à la fois le typage répétitif et les macros difficiles à comprendre et d'éviter d'introduire des définitions de macro dans l'espace général du compilateur.
Donc, dans le fichier d'en-tête:
et l'implémentation cpp est:
Notez le #undef de la macro dès que nous en avons terminé avec elle.
la source
Ma solution, ne pas utiliser boost:
Et voici comment l'utiliser
la source
Un autre en retard à la fête, utilisant le préprocesseur:
(Je viens de mettre des numéros de ligne pour qu'il soit plus facile d'en parler.) Les lignes 1 à 4 sont ce que vous éditez pour définir les éléments de l'énumération. (Je l'ai appelé une "macro de liste", car c'est une macro qui fait une liste de choses. @Lundin m'informe qu'il s'agit d'une technique bien connue appelée X-macros.)
La ligne 7 définit la macro interne afin de remplir la déclaration d'énumération réelle aux lignes 8-11. La ligne 12 annule la définition de la macro interne (juste pour faire taire l'avertissement du compilateur).
La ligne 14 définit la macro interne de manière à créer une version chaîne du nom d'élément enum. Ensuite, les lignes 15 à 18 génèrent un tableau qui peut convertir une valeur d'énumération en chaîne correspondante.
Les lignes 21 à 27 génèrent une fonction qui convertit une chaîne en valeur d'énumération, ou retourne NULL si la chaîne ne correspond à aucune.
C'est un peu encombrant dans la façon dont il gère le 0ème élément. J'ai en fait travaillé autour de cela dans le passé.
J'admets que cette technique dérange les gens qui ne veulent pas penser que le préprocesseur lui-même peut être programmé pour écrire du code à votre place. Je pense que cela illustre fortement la différence entre la lisibilité et la maintenabilité . Le code est difficile à lire, mais si l'énumération contient quelques centaines d'éléments, vous pouvez ajouter, supprimer ou réorganiser des éléments tout en vous assurant que le code généré ne contient aucune erreur.
la source
#define TEST_1 hello #define TEST_2 world
alorstypedef enum { TEST_1, TEST_2 } test_t;
, puis de créer une table de recherche de chaînes qui utilise une macro stringify:const char* table[]= { STRINGIFY(TEST_1), STRINGIFY(TEST_2), };
Il existe déjà plusieurs réponses suggérant des solutions similaires. Beaucoup plus lisible.Voici la méthode Old Skool (utilisée pour être largement utilisée dans gcc) en utilisant uniquement le pré-processeur C. Utile si vous générez des structures de données discrètes mais que vous devez garder l'ordre cohérent entre elles. Les entrées de mylist.tbl peuvent bien sûr être étendues à quelque chose de beaucoup plus complexe.
test.cpp:
Et puis mylist.tbl:
la source
En C ++ comme ceci:
la source
sur http://www.codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C et après
insérer
Fonctionne très bien si les valeurs de l'énumération ne sont pas dupliquées.
Exemple de code pour convertir une valeur d'énumération en chaîne:
Exemple de code pour exactement le contraire:
la source
Merci James pour votre suggestion. C'était très utile, donc j'ai mis en œuvre l'inverse pour contribuer d'une manière ou d'une autre.
la source
Pour étendre la réponse de James, quelqu'un veut un exemple de code pour prendre en charge enum define avec une valeur int, j'ai aussi cette exigence, alors voici ma façon:
La première est la macro d'utilisation interne, qui est utilisée par FOR_EACH:
Et voici la macro de définition:
Donc, lorsque vous l'utilisez, vous aimerez peut-être écrire comme ceci:
qui s'étendra à:
L'idée de base est de définir un SEQ, dont chaque élément est un TUPLE, afin que nous puissions mettre une valeur supplémentaire pour le membre enum. Dans la boucle FOR_EACH, vérifiez la taille de l'élément TUPLE, si la taille est 2, développez le code en KEY = VALUE, sinon gardez simplement le premier élément de TUPLE.
Parce que l'entrée SEQ est en fait TUPLEs, donc si vous voulez définir des fonctions STRINGIZE, vous devrez peut-être pré-traiter les énumérateurs d'entrée, voici la macro pour faire le travail:
La macro
DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ
ne conservera que le premier élément de chaque TUPLE, puis se convertira en SEQ, modifiez maintenant le code de James, vous aurez toute la puissance.Mon implémentation n'est peut-être pas la plus simple, donc si vous ne trouvez pas de code propre, la mienne pour votre référence.
la source
Solution propre et sûre dans le pur standard C:
Production
Raisonnement
Lors de la résolution du problème principal "avoir des constantes d'énumération avec les chaînes correspondantes", un programmeur sensé présentera les exigences suivantes:
La première exigence, et peut-être aussi la seconde, peut être remplie avec diverses solutions macro désordonnées telles que la tristement célèbre astuce "x macro", ou d'autres formes de macro-magie. Le problème avec de telles solutions est qu'elles vous laissent avec un désordre complètement illisible de macros mystérieuses - elles ne répondent pas à la troisième exigence ci-dessus.
La seule chose nécessaire ici est en fait d'avoir une table de recherche de chaînes, à laquelle nous pouvons accéder en utilisant la variable enum comme index. Un tel tableau doit naturellement correspondre directement à l'énumération et vice versa. Lorsque l'un d'eux est mis à jour, l'autre doit également être mis à jour, sinon cela ne fonctionnera pas.
Explication du code
Supposons que nous ayons une énumération comme
Cela peut être changé en
Avec l'avantage que ces constantes de macro peuvent maintenant être utilisées ailleurs, par exemple pour générer une table de recherche de chaînes. La conversion d'une constante de pré-processeur en chaîne peut être effectuée avec une macro "stringify":
Et c'est tout. En utilisant
hello
, nous obtenons la constante enum avec la valeur 0. En utilisant,test_str[hello]
nous obtenons la chaîne "hello".Pour faire correspondre directement l'énumération et la table de recherche, nous devons nous assurer qu'elles contiennent le même nombre d'éléments. Si quelqu'un maintenait le code et ne modifiait que l'énumération, et non la table de recherche, ou vice versa, cette méthode ne fonctionnera pas.
La solution est d'avoir l'énumération pour vous dire combien d'éléments elle contient. Il existe une astuce C couramment utilisée pour cela, ajoutez simplement un élément à la fin, ce qui ne remplit que le but de dire combien d'éléments l'énumération a:
Maintenant, nous pouvons vérifier au moment de la compilation que le nombre d'éléments dans l'énumération est égal au nombre d'éléments dans la table de recherche, de préférence avec une assertion statique C11:
(Il existe également des moyens horribles mais entièrement fonctionnels de créer des assertions statiques dans les anciennes versions du standard C, si quelqu'un insiste pour utiliser des compilateurs de dinosaures. Quant au C ++, il prend également en charge les assertions statiques.)
En remarque, en C11, nous pouvons également obtenir une sécurité de type plus élevée en modifiant la macro stringify:
(
int
car les constantes d'énumération sont en fait de typeint
, nontest_t
)Cela empêchera le code comme
STRINGIFY(random_stuff)
de se compiler.la source
#define
, ajoutez une référence à cette définition dans la déclaration d'énumération et la table de recherche. Si vous faites une gaffe lors de l'ajout de ces lignes, le programme ne se compilera pas. Les chiffres que j'ai ajoutés aux identifiants ne sont en aucun cas obligatoires, vous pouvez aussi bien écrire#define APPLES hello
et#define ORANGES world
suivis detypedef enum { APPES, ORANGES, TEST_N } test_t;
et ainsi de suite.Ce que j'ai fait est une combinaison de ce que j'ai vu ici et dans des questions similaires sur ce site. J'ai fait ceci est Visual Studio 2013. Je ne l'ai pas testé avec d'autres compilateurs.
Tout d'abord, je définis un ensemble de macros qui feront les astuces.
Ensuite, définissez une macro unique qui créera la classe enum et les fonctions pour obtenir les chaînes.
Maintenant, définir un type enum et avoir des chaînes pour cela devient vraiment facile. Tout ce que vous avez à faire est:
Les lignes suivantes peuvent être utilisées pour le tester.
Cela produira:
Je pense qu'il est très propre et facile à utiliser. Il y a quelques limitations:
Si vous pouvez contourner ce problème. Je pense, surtout comment l'utiliser, c'est joli et maigre. Avantages:
la source
Une solution propre à ce problème serait:
La bonne chose à propos de cette solution est qu'elle est simple et que la construction de la fonction peut également être effectuée facilement via copier et remplacer. Notez que si vous allez effectuer beaucoup de conversions et que votre énumération a trop de valeurs possibles, cette solution peut devenir gourmande en ressources processeur.
la source
Je suis un peu en retard mais voici ma solution utilisant g ++ et uniquement des bibliothèques standard. J'ai essayé de minimiser la pollution des espaces de noms et de supprimer tout besoin de retaper les noms d'énumérations.
Le fichier d'en-tête "my_enum.hpp" est:
Exemple d'utilisation:
Cela produira:
Vous n'avez qu'à tout définir une seule fois, votre espace de noms ne doit pas être pollué et tout le calcul n'est fait qu'une seule fois (le reste n'est que des recherches). Cependant, vous n'obtenez pas la sécurité de type des classes enum (ce ne sont encore que des entiers courts), vous ne pouvez pas assigner de valeurs aux enums, vous devez définir des enums quelque part où vous pouvez définir des espaces de noms (par exemple globalement).
Je ne sais pas à quel point les performances sont bonnes ou si c'est une bonne idée (j'ai appris le C avant le C ++, donc mon cerveau fonctionne toujours de cette façon). Si quelqu'un sait pourquoi c'est une mauvaise idée, n'hésitez pas à le signaler.
la source
Nous sommes en 2017 mais la question est toujours vivante
Encore une autre façon:
Les sorties:
la source
Il s'agit d'une version d'énumération étendue de classe élaborée ... elle n'ajoute aucune autre valeur d'énumération autre que celles fournies.
Utilisation: DECLARE_ENUM_SEQ (CameraMode, (3), Fly, FirstPerson, PerspectiveCorrect)
la source
J'avais besoin que cela fonctionne dans les deux sens ET j'intègre fréquemment mes énumérations dans une classe contenant, et j'ai donc commencé avec la solution de James McNellis, bien au sommet de ces réponses, mais j'ai fait cette solution. Notez également que je préfère enum class plutôt que simplement enum, ce qui complique quelque peu la réponse.
Pour l'utiliser dans une classe, vous pouvez faire quelque chose comme ceci:
Et j'ai écrit un test CppUnit, qui montre comment l'utiliser:
Vous devez choisir la macro à utiliser, soit DEFINE_ENUMERATION ou DEFINE_ENUMERATION_INSIDE_CLASS. Vous verrez que j'ai utilisé ce dernier lors de la définition de ComponentStatus :: Status mais j'ai utilisé le premier lors de la définition de Status. La différence est simple. Dans une classe, je préfixe les méthodes to / from comme "static" et si ce n'est pas dans une classe, j'utilise "inline". Des différences triviales, mais nécessaires.
Malheureusement, je ne pense pas qu'il existe un moyen propre d'éviter d'avoir à faire cela:
bien que vous puissiez créer manuellement une méthode en ligne après votre définition de classe qui enchaîne simplement à la méthode de classe, quelque chose comme:
la source
Ma propre réponse, ne pas utiliser boost - utiliser ma propre approche sans magie de définition lourde, et cette solution a une limitation de ne pas pouvoir définir une valeur d'énumération spécifique.
La dernière version peut être trouvée sur github ici:
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/EnumReflect.h
la source
Il y a beaucoup d'autres réponses à cela, mais je pense qu'une meilleure façon est d'utiliser les fonctionnalités de C ++ 17 et d'utiliser constexpr afin que les traductions soient effectuées au moment de la compilation. Il s'agit d'un type sécurisé et nous n'avons pas besoin de jouer avec les macros. Voir ci-dessous:
Cela peut ensuite être facilement utilisé pour que les erreurs de clé de chaîne soient détectées au moment de la compilation:
Le code est plus détaillé que certaines autres solutions, mais nous pouvons facilement effectuer une conversion Enum vers String et une conversion String vers Enum au moment de la compilation et détecter les erreurs de type. Avec certaines des futures fonctionnalités de C ++ 20, cela peut probablement être un peu plus simplifié.
la source