Existe-t-il un moyen simple de convertir l’énumération C ++ en chaîne?

Supposons que nous ayons des énumérations nommées:

enum MyEnum { FOO, BAR = 0x50 }; 

Ce que je recherche sur Google est un script (n’importe quel langage) qui parsing tous les en-têtes de mon projet et génère un en-tête avec une fonction par enum.

 char* enum_to_ssortingng(MyEnum t); 

Et une implémentation avec quelque chose comme ça:

 char* enum_to_ssortingng(MyEnum t){ switch(t){ case FOO: return "FOO"; case BAR: return "BAR"; default: return "INVALID ENUM"; } } 

Le gotcha est vraiment avec des énumérations typées et des énumérations de style C sans nom. Est-ce que quelqu’un sait quelque chose pour ça?

EDIT: La solution ne doit pas modifier ma source, sauf pour les fonctions générées. Les énumérations sont dans une API, donc l’utilisation des solutions proposées jusqu’à présent n’est tout simplement pas une option.

Vous voudrez peut-être vérifier GCCXML .

L’exécution de GCCXML sur votre exemple de code produit:

          

Vous pouvez utiliser n’importe quel langage que vous préférez pour extraire les balises Enumeration et EnumValue et générer le code souhaité.

Les macros X sont la meilleure solution. Exemple:

 #include  enum Colours { # define X(a) a, # include "colours.def" # undef X ColoursCount }; char const* const colours_str[] = { # define X(a) #a, # include "colours.def" # undef X 0 }; std::ostream& operator<<(std::ostream& os, enum Colours c) { if (c >= ColoursCount || c < 0) return os << "???"; return os << colours_str[c]; } int main() { std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl; } 

colours.def:

 X(Red) X(Green) X(Blue) X(Cyan) X(Yellow) X(Magenta) 

Cependant, je préfère généralement la méthode suivante, de sorte qu'il est possible de modifier légèrement la chaîne.

 #define X(a, b) a, #define X(a, b) b, X(Red, "red") X(Green, "green") // etc. 

@hydroo: Sans le fichier supplémentaire:

 #define SOME_ENUM(DO) \ DO(Foo) \ DO(Bar) \ DO(Baz) #define MAKE_ENUM(VAR) VAR, enum MetaSyntacticVariable{ SOME_ENUM(MAKE_ENUM) }; #define MAKE_STRINGS(VAR) #VAR, const char* const MetaSyntacticVariableNames[] = { SOME_ENUM(MAKE_STRINGS) }; 

Ce que j’ai tendance à faire est de créer un tableau C avec les noms dans le même ordre et la même position que les valeurs enum.

par exemple.

 enum colours { red, green, blue }; const char *colour_names[] = { "red", "green", "blue" }; 

alors vous pouvez utiliser le tableau dans des endroits où vous voulez une valeur lisible par l’homme, par exemple

 colours mycolour = red; cout << "the colour is" << colour_names[mycolour]; 

Vous pourriez expérimenter un peu avec l'opérateur de ssortingngizing (voir # dans votre référence de préprocesseur) qui fera ce que vous voulez, dans certaines circonstances, par exemple:

 #define printword(XX) cout << #XX; printword(red); 

va imprimer "rouge" à stdout. Malheureusement, cela ne fonctionnera pas pour une variable (car le nom de la variable sera imprimé)

J’ai une macro incroyablement simple à utiliser qui le fait de manière complètement sèche. Il s’agit de macros variadiques et d’une simple magie d’parsing. Voici:

 #define AWESOME_MAKE_ENUM(name, ...) enum class name { __VA_ARGS__, __COUNT}; \ inline std::ostream& operator<<(std::ostream& os, name value) { \ std::string enumName = #name; \ std::string str = #__VA_ARGS__; \ int len = str.length(); \ std::vector ssortingngs; \ std::ossortingngstream temp; \ for(int i = 0; i < len; i ++) { \ if(isspace(str[i])) continue; \ else if(str[i] == ',') { \ strings.push_back(temp.str()); \ temp.str(std::string());\ } \ else temp<< str[i]; \ } \ strings.push_back(temp.str()); \ os << enumName << "::" << strings[static_cast(value)]; \ return os;} 

Pour l’utiliser dans votre code, faites simplement:

 AWESOME_MAKE_ENUM(Animal, DOG, CAT, HORSE ); 

QT est capable de tirer celui de (grâce au compilateur d’objects méta): link

Je viens de réinventer cette roue aujourd’hui et j’ai pensé la partager.

Cette implémentation ne nécessite aucune modification du code qui définit les constantes, qui peuvent être des énumérations ou des #define s ou toute autre chose qui revient à un entier – dans mon cas, des symboles ont été définis en termes d’autres symboles. Cela fonctionne également bien avec des valeurs rares. Il permet même plusieurs noms pour la même valeur, renvoyant toujours le premier. Le seul inconvénient est qu’il vous oblige à créer un tableau des constantes, qui peuvent devenir obsolètes au fur et à mesure que de nouvelles sont ajoutées, par exemple.

 struct IdAndName { int id; const char * name; bool operator<(const IdAndName &rhs) const { return id < rhs.id; } }; #define ID_AND_NAME(x) { x, #x } const char * IdToName(int id, IdAndName *table_begin, IdAndName *table_end) { if ((table_end - table_begin) > 1 && table_begin[0].id > table_begin[1].id) std::stable_sort(table_begin, table_end); IdAndName searchee = { id, NULL }; IdAndName *p = std::lower_bound(table_begin, table_end, searchee); return (p == table_end || p->id != id) ? NULL : p->name; } template const char * IdToName(int id, IdAndName (&table)[N]) { return IdToName(id, &table[0], &table[N]); } 

Un exemple de la façon dont vous l’utiliseriez:

 static IdAndName WindowsErrorTable[] = { ID_AND_NAME(INT_MAX), // flag value to indicate unsorted table ID_AND_NAME(NO_ERROR), ID_AND_NAME(ERROR_INVALID_FUNCTION), ID_AND_NAME(ERROR_FILE_NOT_FOUND), ID_AND_NAME(ERROR_PATH_NOT_FOUND), ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES), ID_AND_NAME(ERROR_ACCESS_DENIED), ID_AND_NAME(ERROR_INVALID_HANDLE), ID_AND_NAME(ERROR_ARENA_TRASHED), ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY), ID_AND_NAME(ERROR_INVALID_BLOCK), ID_AND_NAME(ERROR_BAD_ENVIRONMENT), ID_AND_NAME(ERROR_BAD_FORMAT), ID_AND_NAME(ERROR_INVALID_ACCESS), ID_AND_NAME(ERROR_INVALID_DATA), ID_AND_NAME(ERROR_INVALID_DRIVE), ID_AND_NAME(ERROR_CURRENT_DIRECTORY), ID_AND_NAME(ERROR_NOT_SAME_DEVICE), ID_AND_NAME(ERROR_NO_MORE_FILES) }; const char * error_name = IdToName(GetLastError(), WindowsErrorTable); 

La fonction IdToName repose sur std::lower_bound pour effectuer des recherches rapides, ce qui nécessite le sorting de la table. Si les deux premières entrées du tableau sont hors d’usage, la fonction les sortingera automatiquement.

Edit: Un commentaire m’a fait penser à une autre façon d’utiliser le même principe. Une macro simplifie la génération d’un énoncé de switch important.

 #define ID_AND_NAME(x) case x: return #x const char * WindowsErrorToName(int id) { switch(id) { ID_AND_NAME(ERROR_INVALID_FUNCTION); ID_AND_NAME(ERROR_FILE_NOT_FOUND); ID_AND_NAME(ERROR_PATH_NOT_FOUND); ID_AND_NAME(ERROR_TOO_MANY_OPEN_FILES); ID_AND_NAME(ERROR_ACCESS_DENIED); ID_AND_NAME(ERROR_INVALID_HANDLE); ID_AND_NAME(ERROR_ARENA_TRASHED); ID_AND_NAME(ERROR_NOT_ENOUGH_MEMORY); ID_AND_NAME(ERROR_INVALID_BLOCK); ID_AND_NAME(ERROR_BAD_ENVIRONMENT); ID_AND_NAME(ERROR_BAD_FORMAT); ID_AND_NAME(ERROR_INVALID_ACCESS); ID_AND_NAME(ERROR_INVALID_DATA); ID_AND_NAME(ERROR_INVALID_DRIVE); ID_AND_NAME(ERROR_CURRENT_DIRECTORY); ID_AND_NAME(ERROR_NOT_SAME_DEVICE); ID_AND_NAME(ERROR_NO_MORE_FILES); default: return NULL; } } 
 #define ssortingngify( name ) # name enum MyEnum { ENUMVAL1 }; ...stuff... ssortingngify(EnumName::ENUMVAL1); // Returns MyEnum::ENUMVAL1 

Discussion supplémentaire sur cette méthode

Les directives de préprocesseur pour les nouveaux venus

Intéressant de voir le nombre de façons. voici celui que j’ai utilisé il y a longtemps:

dans le fichier myenummap.h:

 #include  #include  enum test{ one, two, three, five=5, six, seven }; struct mymap : std::map { mymap() { this->operator[]( one ) = "ONE"; this->operator[]( two ) = "TWO"; this->operator[]( three ) = "THREE"; this->operator[]( five ) = "FIVE"; this->operator[]( six ) = "SIX"; this->operator[]( seven ) = "SEVEN"; }; ~mymap(){}; }; 

dans main.cpp

 #include "myenummap.h" ... mymap nummap; std::cout<< nummap[ one ] << std::endl; 

Ce n'est pas const, mais c'est pratique.

Voici une autre manière d'utiliser les fonctionnalités C ++ 11. Ceci est const, n'hérite pas d'un conteneur STL et est un peu plus ordonné:

 #include  #include  #include  #include  //These stay together and must be modified together enum test{ one, two, three, five=5, six, seven }; std::ssortingng enum_to_str(test const& e) { typedef std::pair mapping; auto m = [](test const& e,std::ssortingng const& s){return mapping(static_cast(e),s);}; std::vector const nummap = { m(one,"one"), m(two,"two"), m(three,"three"), m(five,"five"), m(six,"six"), m(seven,"seven"), }; for(auto i : nummap) { if(i.first==static_cast(e)) { return i.second; } } return ""; } int main() { // std::cout<< enum_to_str( 46 ) << std::endl; //compilation will fail std::cout<< "Invalid enum to string : [" << enum_to_str( test(46) ) << "]"< 
 #include  #include  #include  #include  #include  #include  #define SMART_ENUM(EnumName, ...) \ class EnumName \ { \ private: \ static std::map nameMap; \ public: \ enum {__VA_ARGS__}; \ private: \ static std::map initMap() \ { \ using namespace std; \ \ int val = 0; \ ssortingng buf_1, buf_2, str = #__VA_ARGS__; \ replace(str.begin(), str.end(), '=', ' '); \ ssortingngstream stream(str); \ vector ssortingngs; \ while (getline(stream, buf_1, ',')) \ ssortingngs.push_back(buf_1); \ map tmp; \ for(vector::iterator it = ssortingngs.begin(); \ it != ssortingngs.end(); \ ++it) \ { \ buf_1.clear(); buf_2.clear(); \ ssortingngstream localStream(*it); \ localStream>> buf_1 >> buf_2; \ if(buf_2.size() > 0) \ val = atoi(buf_2.c_str()); \ tmp[val++] = buf_1; \ } \ return tmp; \ } \ public: \ static std::ssortingng toSsortingng(int aInt) \ { \ return nameMap[aInt]; \ } \ }; \ std::map \ EnumName::nameMap = EnumName::initMap(); 

Usage:

 SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN) cout< 

Cela peut être fait en C ++ 11

 #include  enum MyEnum { AA, BB, CC, DD }; static std::map< MyEnum, const char * > info = { {AA, "This is an apple"}, {BB, "This is a book"}, {CC, "This is a coffee"}, {DD, "This is a door"} }; void main() { std::cout << info[AA] << endl << info[BB] << endl << info[CC] << endl << info[DD] << endl; } 

Autre réponse: dans certains contextes, il est judicieux de définir votre énumération dans un format non-code, tel qu’un fichier CSV, YAML ou XML, puis de générer le code d’énumération C ++ et le code de chaîne de la définition. Cette approche peut être ou ne pas être pratique dans votre application, mais il faut en tenir compte.

Ceci est une modification de @ user3360260 answer. Il a les nouvelles fonctionnalités suivantes

  • MyEnum fromSsortingng(const ssortingng&) support
  • comstack avec VisualStudio 2012
  • l’énumération est un type POD réel (pas seulement des déclarations const), vous pouvez donc l’assigner à une variable.
  • ajout d’une fonctionnalité “range” en C ++ (sous forme de vecteur) pour permettre une itération “foreach” sur enum

Usage:

 SMART_ENUM(MyEnum, ONE=1, TWO, THREE, TEN=10, ELEVEN) MyEnum foo = MyEnum::TWO; cout << MyEnum::toString(foo); // static method cout << foo.toString(); // member method cout << MyEnum::toString(MyEnum::TWO); cout << MyEnum::toString(10); MyEnum foo = myEnum::fromString("TWO"); // C++11 iteration over all values for( auto x : MyEnum::allValues() ) { cout << x.toString() << endl; } 

Voici le code

 #define SMART_ENUM(EnumName, ...) \ class EnumName \ { \ public: \ EnumName() : value(0) {} \ EnumName(int x) : value(x) {} \ public: \ enum {__VA_ARGS__}; \ private: \ static void initMap(std::map& tmp) \ { \ using namespace std; \ \ int val = 0; \ ssortingng buf_1, buf_2, str = #__VA_ARGS__; \ replace(str.begin(), str.end(), '=', ' '); \ ssortingngstream stream(str); \ vector ssortingngs; \ while (getline(stream, buf_1, ',')) \ ssortingngs.push_back(buf_1); \ for(vector::iterator it = ssortingngs.begin(); \ it != ssortingngs.end(); \ ++it) \ { \ buf_1.clear(); buf_2.clear(); \ ssortingngstream localStream(*it); \ localStream>> buf_1 >> buf_2; \ if(buf_2.size() > 0) \ val = atoi(buf_2.c_str()); \ tmp[val++] = buf_1; \ } \ } \ int value; \ public: \ operator int () const { return value; } \ std::ssortingng toSsortingng(void) const { \ return toSsortingng(value); \ } \ static std::ssortingng toSsortingng(int aInt) \ { \ return nameMap()[aInt]; \ } \ static EnumName fromSsortingng(const std::ssortingng& s) \ { \ auto it = find_if(nameMap().begin(), nameMap().end(), [s](const std::pair& p) { \ return p.second == s; \ }); \ if (it == nameMap().end()) { \ /*value not found*/ \ throw EnumName::Exception(); \ } else { \ return EnumName(it->first); \ } \ } \ class Exception : public std::exception {}; \ static std::map& nameMap() { \ static std::map nameMap0; \ if (nameMap0.size() ==0) initMap(nameMap0); \ return nameMap0; \ } \ static std::vector allValues() { \ std::vector x{ __VA_ARGS__ }; \ return x; \ } \ bool operator<(const EnumName a) const { return (int)*this < (int)a; } \ }; 

Notez que la conversion vers Chaîne est une recherche rapide, alors que la conversion depuis Chaîne est une recherche linéaire lente. Mais les chaînes sont très chères quand même (et le fichier IO associé), je n'ai pas ressenti le besoin d'optimiser ou d'utiliser un bimap.

La solution macro de Suma est sympa. Vous n’avez pas besoin d’avoir deux macro différentes, cependant. C ++ inclura volontiers un en-tête deux fois. Il suffit de laisser de côté la garde incluse.

Donc, vous auriez un foobar.h définissant juste

 ENUM(Foo, 1) ENUM(Bar, 2) 

et vous l’incluriez comme ceci:

 #define ENUMFACTORY_ARGUMENT "foobar.h" #include "enumfactory.h" 

enumfactory.h fera 2 #include ENUMFACTORY_ARGUMENT s. Au premier tour, il élargit ENUM comme DECLARE_ENUM de Suma; Au deuxième tour, ENUM fonctionne comme DEFINE_ENUM .

Vous pouvez également inclure enumfactory.h plusieurs fois, à condition que vous passiez des définitions différentes pour ENUMFACTORY_ARGUMENT

Notez que votre fonction de conversion devrait idéalement retourner un const char *.

Si vous pouvez vous permettre de mettre vos enums dans leurs fichiers d’en-tête séparés, vous pourriez peut-être faire quelque chose comme ça avec des macros (oh, ce sera moche):

 #include "enum_def.h" #include "colour.h" #include "enum_conv.h" #include "colour.h" 

Où enum_def.h a:

 #undef ENUM_START #undef ENUM_ADD #undef ENUM_END #define ENUM_START(NAME) enum NAME { #define ENUM_ADD(NAME, VALUE) NAME = VALUE, #define ENUM_END }; 

Et enum_conv.h a:

 #undef ENUM_START #undef ENUM_ADD #undef ENUM_END #define ENUM_START(NAME) const char *##NAME##_to_ssortingng(NAME val) { switch (val) { #define ENUM_ADD(NAME, VALUE) case NAME: return #NAME; #define ENUM_END default: return "Invalid value"; } } 

Et enfin, la couleur.h a:

 ENUM_START(colour) ENUM_ADD(red, 0xff0000) ENUM_ADD(green, 0x00ff00) ENUM_ADD(blue, 0x0000ff) ENUM_END 

Et vous pouvez utiliser la fonction de conversion comme:

 printf("%s", colour_to_ssortingng(colour::red)); 

C’est moche, mais c’est le seul moyen (au niveau du préprocesseur) qui vous permet de définir votre enum en un seul endroit dans votre code. Votre code n’est donc pas sujet aux erreurs dues aux modifications apscopes à l’énumération. Votre définition enum et la fonction de conversion seront toujours synchronisées. Cependant, je le répète, c’est moche 🙂

Je le fais avec des classes wrapper côte à côte séparées qui sont générées avec des macros. Il y a plusieurs avantages:

  • Je peux les générer pour des énumérations que je ne définis pas
  • Peut incorporer une vérification de plage dans la classe wrapper
  • Peut effectuer un formatage “plus intelligent” avec les énumérations de champs de bits

L’inconvénient, bien sûr, est que je dois dupliquer les valeurs d’énumération dans les classes de formateur, et je n’ai aucun script pour les générer. En dehors de cela, cependant, cela semble fonctionner plutôt bien.

Voici un exemple d’une énumération de mon code, sans tout le code d’infrastructure qui implémente les macros et les modèles, mais vous pouvez avoir l’idée suivante:

 enum EHelpLocation { HELP_LOCATION_UNKNOWN = 0, HELP_LOCAL_FILE = 1, HELP_HTML_ONLINE = 2, }; class CEnumFormatter_EHelpLocation : public CEnumDefaultFormatter< EHelpLocation > { public: static inline CSsortingng FormatEnum( EHelpLocation eValue ) { switch ( eValue ) { ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCATION_UNKNOWN ); ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_LOCAL_FILE ); ON_CASE_VALUE_RETURN_STRING_OF_VALUE( HELP_HTML_ONLINE ); default: return FormatAsNumber( eValue ); } } }; DECLARE_RANGE_CHECK_CLASS( EHelpLocation, CRangeInfoSequential< HELP_HTML_ONLINE > ); typedef ESmartEnum< EHelpLocation, HELP_LOCATION_UNKNOWN, CEnumFormatter_EHelpLocation, CRangeInfo_EHelpLocation > SEHelpLocation; 

L’idée est alors qu’au lieu d’utiliser EHelpLocation, vous utilisez SEHelpLocation; tout fonctionne de la même manière, mais vous obtenez une vérification de plage et une méthode «Format ()» sur la variable enum elle-même. Si vous devez formater une valeur autonome, vous pouvez utiliser CEnumFormatter_EHelpLocation :: FormatEnum (…).

J’espère que c’est utile. Je me rends compte que cela ne répond pas non plus à la question initiale sur un script pour générer l’autre classe, mais j’espère que la structure aidera quelqu’un à essayer de résoudre le même problème ou à écrire un tel script.

Voici une solution à un fichier (basée sur la réponse élégante de @Marcin:

 #include  #define ENUM_TXT \ X(Red) \ X(Green) \ X(Blue) \ X(Cyan) \ X(Yellow) \ X(Magenta) \ enum Colours { # define X(a) a, ENUM_TXT # undef X ColoursCount }; char const* const colours_str[] = { # define X(a) #a, ENUM_TXT # undef X 0 }; std::ostream& operator<<(std::ostream& os, enum Colours c) { if (c >= ColoursCount || c < 0) return os << "???"; return os << colours_str[c] << std::endl; } int main() { std::cout << Red << Blue << Green << Cyan << Yellow << Magenta << std::endl; } 

C’était ma solution avec BOOST:

 #include  #define X_STR_ENUM_TOSTRING_CASE(r, data, elem) \ case elem : return BOOST_PP_STRINGIZE(elem); #define X_ENUM_STR_TOENUM_IF(r, data, elem) \ else if(data == BOOST_PP_STRINGIZE(elem)) return elem; #define STR_ENUM(name, enumerators) \ enum name { \ BOOST_PP_SEQ_ENUM(enumerators) \ }; \ \ inline const QSsortingng enumToStr(name v) \ { \ switch (v) \ { \ BOOST_PP_SEQ_FOR_EACH( \ X_STR_ENUM_TOSTRING_CASE, \ name, \ enumerators \ ) \ \ default: \ return "[Unknown " BOOST_PP_STRINGIZE(name) "]"; \ } \ } \ \ template  \ inline const T strToEnum(QSsortingng v); \ \ template <> \ inline const name strToEnum(QString v) \ { \ if(v=="") \ throw std::runtime_error("Empty enum value"); \ \ BOOST_PP_SEQ_FOR_EACH( \ X_ENUM_STR_TOENUM_IF, \ v, \ enumerators \ ) \ \ else \ throw std::runtime_error( \ QString("[Unknown value %1 for enum %2]") \ .arg(v) \ .arg(BOOST_PP_STRINGIZE(name)) \ .toStdString().c_str()); \ } 

Pour créer enum, déclarez:

 STR_ENUM ( SERVICE_RELOAD, (reload_log) (reload_settings) (reload_qxml_server) ) 

Pour les conversions:

 SERVICE_RELOAD serviceReloadEnum = strToEnum("reload_log"); QSsortingng serviceReloadStr = enumToStr(reload_log); 

Un problème avec la réponse 0 est que les valeurs binarys ne commencent pas nécessairement à 0 et ne sont pas nécessairement contiguës.

Quand j’en ai besoin, je:

  • tirer la définition enum dans ma source
  • modifiez-le pour obtenir uniquement les noms
  • faire une macro pour changer le nom à la clause case dans la question, mais généralement sur une seule ligne: case foo: return “foo”;
  • append le commutateur, la syntaxe par défaut et toute autre syntaxe pour le rendre légal

Le script ruby ​​suivant tente d’parsingr les en-têtes et construit les sources requirejses avec les en-têtes d’origine.

 #! /usr/bin/env ruby # Let's "parse" the headers # Note that using a regular expression is rather fragile # and may break on some inputs GLOBS = [ "toto/*.h", "tutu/*.h", "tutu/*.hxx" ] enums = {} GLOBS.each { |glob| Dir[glob].each { |header| enums[header] = File.open(header, 'rb') { |f| f.read }.scan(/enum\s+(\w+)\s+\{\s*([^}]+?)\s*\}/m).collect { |enum_name, enum_key_and_values| [ enum_name, enum_key_and_values.split(/\s*,\s*/).collect { |enum_key_and_value| enum_key_and_value.split(/\s*=\s*/).first } ] } } } # Now we build a .h and .cpp alongside the parsed headers # using the template engine provided with ruby require 'erb' template_h = ERB.new <<-EOS #ifndef <%= enum_name %>_to_ssortingng_h_ #define <%= enum_name %>_to_ssortingng_h_ 1 #include "<%= header %>" char* enum_to_ssortingng(<%= enum_name %> e); #endif EOS template_cpp = ERB.new <<-EOS #include "<%= enum_name %>_to_ssortingng.h" char* enum_to_ssortingng(<%= enum_name %> e) { switch (e) {<% enum_keys.each do |enum_key| %> case <%= enum_key %>: return "<%= enum_key %>";<% end %> default: return "INVALID <%= enum_name %> VALUE"; } } EOS enums.each { |header, enum_name_and_keys| enum_name_and_keys.each { |enum_name, enum_keys| File.open("#{File.dirname(header)}/#{enum_name}_to_ssortingng.h", 'wb') { |built_h| built_h.write(template_h.result(binding)) } File.open("#{File.dirname(header)}/#{enum_name}_to_ssortingng.cpp", 'wb') { |built_cpp| built_cpp.write(template_cpp.result(binding)) } } } 

Utiliser des expressions régulières rend cet “parsingur” assez fragile, il ne pourra peut-être pas gérer correctement vos en-têtes.

Disons que vous avez un en-tête toto / ah, contenant des définitions pour les énumérations MyEnum et MyEnum2. Le script va construire:

 toto/MyEnum_to_ssortingng.h toto/MyEnum_to_ssortingng.cpp toto/MyEnum2_to_ssortingng.h toto/MyEnum2_to_ssortingng.cpp 

Des solutions plus robustes seraient:

  • Construisez toutes les sources définissant les énumérations et leurs opérations à partir d’une autre source. Cela signifie que vous définirez vos énumérations dans un fichier XML / YML / quelque chose de beaucoup plus facile à parsingr que C / C ++.
  • Utilisez un véritable compilateur tel que suggéré par Avdi.
  • Utilisez des macros de préprocesseur avec ou sans modèles.

C’est un logiciel inédit, mais il semble que BOOST_ENUM de Frank Laub pourrait convenir. La partie que j’aime à ce sujet est que vous pouvez définir un enum dans le cadre d’une classe que la plupart des énumérations basées sur Macro ne vous permettent généralement pas de faire. Il est situé dans le Boost Vault à l’ adresse suivante : http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& Il n’y a eu aucun développement depuis 2006, donc je ne le fais pas savoir comment il comstack avec les nouvelles versions de Boost. Regardez sous libs / test pour un exemple d’utilisation.

C’est à peu près la seule façon de le faire (un tableau de chaînes pourrait également fonctionner).

Le problème est que, une fois un programme C compilé, la valeur binary de l’enum est tout ce qui est utilisé, et le nom a disparu.

Voici un programme CLI que j’ai écrit pour convertir facilement des énumérations en chaînes. Son facile à utiliser, et prend environ 5 secondes pour le faire (y compris le temps de cd dans le répertoire contenant le programme, puis exécutez-le, en lui passant le fichier contenant l’énumération).

Téléchargez ici: http://www.mediafire.com/?nttignoozzz

Sujet de discussion à ce sujet ici: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtossortingngs.html

Exécutez le programme avec l’argument “–help” pour obtenir une description de son utilisation.

Il n’y a pas si longtemps, j’ai fait quelques astuces pour afficher correctement les énumérations dans QComboBox et avoir la définition des représentations enum et ssortingng en une seule instruction

 #pragma once #include  namespace enumeration { struct enumerator_base : boost::noncopyable { typedef boost::unordered_map kv_storage_t; typedef kv_storage_t::value_type kv_type; kv_storage_t const & kv() const { return storage_; } LPCWSTR name(int i) const { kv_storage_t::const_iterator it = storage_.find(i); if(it != storage_.end()) return it->second.c_str(); return L"empty"; } protected: kv_storage_t storage_; }; template struct enumerator; template struct enum_singleton : enumerator_base { static enumerator_base const & instance() { static D inst; return inst; } }; } #define QENUM_ENTRY(K, V, N) K, N storage_.insert(std::make_pair((int)K, V)); #define QBEGIN_ENUM(NAME, C) \ enum NAME \ { \ C \ } \ }; \ } \ #define QEND_ENUM(NAME) \ }; \ namespace enumeration \ { \ template<> \ struct enumerator\ : enum_singleton< enumerator >\ { \ enumerator() \ { //usage /* QBEGIN_ENUM(test_t, QENUM_ENTRY(test_entry_1, L"number uno", QENUM_ENTRY(test_entry_2, L"number dos", QENUM_ENTRY(test_entry_3, L"number tres", QEND_ENUM(test_t))))) */ 

Maintenant vous avez enumeration::enum_singleton::instance() capable de convertir des enums en chaînes. Si vous remplacez kv_storage_t par boost::bimap , vous pourrez également effectuer une conversion en boost::bimap . La classe de base commune au convertisseur a été introduite pour le stocker dans l’object Qt, car les objects Qt ne pouvaient pas être des modèles

Apparence précédente

En variante, utilisez simple lib> http://codeproject.com/Articles/42035/Enum-to-Ssortingng-and-Vice-Versa-in-C

Dans le code

 #include  enum FORM { F_NONE = 0, F_BOX, F_CUBE, F_SPHERE, }; 

append des lignes

 Begin_Enum_Ssortingng( FORM ) { Enum_Ssortingng( F_NONE ); Enum_Ssortingng( F_BOX ); Enum_Ssortingng( F_CUBE ); Enum_Ssortingng( F_SPHERE ); } End_Enum_Ssortingng; 

Travaillez bien, si les valeurs enum ne sont pas dupliquées .

Exemple d’utilisation

 enum FORM f = ... const std::ssortingng& str = EnumSsortingng< FORM >::From( f ); 

et vice versa

 assert( EnumSsortingng< FORM >::To( f, str ) ); 

Voici une tentative pour obtenir automatiquement les opérateurs de stream << et >> sur enum avec une commande de macro à une ligne uniquement …

Définitions:

 #include  #include  #include  #include  #include  #include  #include  #define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__) #define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__) #define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__) #define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__) #define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__) #define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__) #define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__) #define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__) #define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__) #define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__) #define MAKE_STRING10_(str) #str #define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__) #define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__) #define MAKE_ENUM_(atsortingbute, name, ...) name { __VA_ARGS__ }; \ atsortingbute std::istream& operator>>(std::istream& is, name& e) { \ const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \ std::ssortingng str; \ std::istream& r = is >> str; \ const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \ const std::vector enumStr(name##Str, name##Str + len); \ const std::vector::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \ if (it != enumStr.end())\ e = name(it - enumStr.begin()); \ else \ throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \ return r; \ }; \ atsortingbute std::ostream& operator<<(std::ostream& os, const name& e) { \ const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \ return (os << name##Str[e]); \ } 

Usage:

 // Declare global enum enum MAKE_ENUM(Test3, Item13, Item23, Item33, Itdsdgem43); class Essai { public: // Declare enum inside class enum MAKE_CLASS_ENUM(Test, Item1, Item2, Item3, Itdsdgem4); }; int main() { std::cout << Essai::Item1 << std::endl; Essai::Test ddd = Essai::Item1; std::cout << ddd << std::endl; std::istringstream strm("Item2"); strm >> ddd; std::cout << (int) ddd << std::endl; std::cout << ddd << std::endl; } 

Not sure about the limitations of this scheme though... comments are welcome!

 #include  #include  #define IDMAP(x) (x,#x) std::map enToStr; class mapEnumtoSsortingng { public: mapEnumtoSsortingng(){ } mapEnumtoSsortingng& operator()(int i,std::ssortingng str) { enToStr[i] = str; return *this; } public: std::ssortingng operator [] (int i) { return enToStr[i]; } }; mapEnumtoSsortingng k; mapEnumtoSsortingng& init() { return k; } int main() { init() IDMAP(1) IDMAP(2) IDMAP(3) IDMAP(4) IDMAP(5); std::cout< 

Check this post:

Class implementation of C++ Enums

it contains class implementation of c++ enum.

I want to post this in case someone finds it useful.

In my case, I simply need to generate ToSsortingng() and FromSsortingng() functions for a single C++11 enum from a single .hpp file.

I wrote a python script that parses the header file containing the enum items and generates the functions in a new .cpp file.

You can add this script in CMakeLists.txt with execute_process , or as a pre-build event in Visual Studio. The .cpp file will be automatically generated, without the need to manually update it each time a new enum item is added.

generate_enum_ssortingngs.py

 # This script is used to generate ssortingngs from C++ enums import re import sys import os fileName = sys.argv[1] enumName = os.path.basename(os.path.splitext(fileName)[0]) with open(fileName, 'r') as f: content = f.read().replace('\n', '') searchResult = re.search('enum(.*)\{(.*?)\};', content) tokens = searchResult.group(2) tokens = tokens.split(',') tokens = map(str.ssortingp, tokens) tokens = map(lambda token: re.search('([a-zA-Z0-9_]*)', token).group(1), tokens) textOut = '' textOut += '\n#include "' + enumName + '.hpp"\n\n' textOut += 'namespace myns\n' textOut += '{\n' textOut += ' std::ssortingng ToSsortingng(ErrorCode errorCode)\n' textOut += ' {\n' textOut += ' switch (errorCode)\n' textOut += ' {\n' for token in tokens: textOut += ' case ' + enumName + '::' + token + ':\n' textOut += ' return "' + token + '";\n' textOut += ' default:\n' textOut += ' return "Last";\n' textOut += ' }\n' textOut += ' }\n' textOut += '\n' textOut += ' ' + enumName + ' FromSsortingng(const std::ssortingng &errorCode)\n' textOut += ' {\n' textOut += ' if ("' + tokens[0] + '" == errorCode)\n' textOut += ' {\n' textOut += ' return ' + enumName + '::' + tokens[0] + ';\n' textOut += ' }\n' for token in tokens[1:]: textOut += ' else if("' + token + '" == errorCode)\n' textOut += ' {\n' textOut += ' return ' + enumName + '::' + token + ';\n' textOut += ' }\n' textOut += '\n' textOut += ' return ' + enumName + '::Last;\n' textOut += ' }\n' textOut += '}\n' fileOut = open(enumName + '.cpp', 'w') fileOut.write(textOut) 

Exemple:

ErrorCode.hpp

 #pragma once #include  #include  namespace myns { enum class ErrorCode : uint32_t { OK = 0, OutOfSpace, ConnectionFailure, InvalidJson, DatabaseFailure, HttpError, FileSystemError, FailedToEncrypt, FailedToDecrypt, EndOfFile, FailedToOpenFileForRead, FailedToOpenFileForWrite, FailedToLaunchProcess, Last }; std::ssortingng ToSsortingng(ErrorCode errorCode); ErrorCode FromSsortingng(const std::ssortingng &errorCode); } 

Run python generate_enum_ssortingngs.py ErrorCode.hpp

Résultat:

ErrorCode.cpp

 #include "ErrorCode.hpp" namespace myns { std::ssortingng ToSsortingng(ErrorCode errorCode) { switch (errorCode) { case ErrorCode::OK: return "OK"; case ErrorCode::OutOfSpace: return "OutOfSpace"; case ErrorCode::ConnectionFailure: return "ConnectionFailure"; case ErrorCode::InvalidJson: return "InvalidJson"; case ErrorCode::DatabaseFailure: return "DatabaseFailure"; case ErrorCode::HttpError: return "HttpError"; case ErrorCode::FileSystemError: return "FileSystemError"; case ErrorCode::FailedToEncrypt: return "FailedToEncrypt"; case ErrorCode::FailedToDecrypt: return "FailedToDecrypt"; case ErrorCode::EndOfFile: return "EndOfFile"; case ErrorCode::FailedToOpenFileForRead: return "FailedToOpenFileForRead"; case ErrorCode::FailedToOpenFileForWrite: return "FailedToOpenFileForWrite"; case ErrorCode::FailedToLaunchProcess: return "FailedToLaunchProcess"; case ErrorCode::Last: return "Last"; default: return "Last"; } } ErrorCode FromSsortingng(const std::ssortingng &errorCode) { if ("OK" == errorCode) { return ErrorCode::OK; } else if("OutOfSpace" == errorCode) { return ErrorCode::OutOfSpace; } else if("ConnectionFailure" == errorCode) { return ErrorCode::ConnectionFailure; } else if("InvalidJson" == errorCode) { return ErrorCode::InvalidJson; } else if("DatabaseFailure" == errorCode) { return ErrorCode::DatabaseFailure; } else if("HttpError" == errorCode) { return ErrorCode::HttpError; } else if("FileSystemError" == errorCode) { return ErrorCode::FileSystemError; } else if("FailedToEncrypt" == errorCode) { return ErrorCode::FailedToEncrypt; } else if("FailedToDecrypt" == errorCode) { return ErrorCode::FailedToDecrypt; } else if("EndOfFile" == errorCode) { return ErrorCode::EndOfFile; } else if("FailedToOpenFileForRead" == errorCode) { return ErrorCode::FailedToOpenFileForRead; } else if("FailedToOpenFileForWrite" == errorCode) { return ErrorCode::FailedToOpenFileForWrite; } else if("FailedToLaunchProcess" == errorCode) { return ErrorCode::FailedToLaunchProcess; } else if("Last" == errorCode) { return ErrorCode::Last; } return ErrorCode::Last; } } 

Well, yet another option. A typical use case is where you need constant for the HTTP verbs as well as using is ssortingng version values.

The example:

 int main () { VERB a = VERB::GET; VERB b = VERB::GET; VERB c = VERB::POST; VERB d = VERB::PUT; VERB e = VERB::DELETE; std::cout << a.toString() << std::endl; std::cout << a << std::endl; if ( a == VERB::GET ) { std::cout << "yes" << std::endl; } if ( a == b ) { std::cout << "yes" << std::endl; } if ( a != c ) { std::cout << "no" << std::endl; } } 

The VERB class:

 // ----------------------------------------------------------- // ----------------------------------------------------------- class VERB { private: // private constants enum Verb {GET_=0, POST_, PUT_, DELETE_}; // private ssortingng values static const std::ssortingng theSsortingngs[]; // private value const Verb value; const std::ssortingng text; // private constructor VERB (Verb v) : value(v), text (theSsortingngs[v]) { // std::cout << " constructor \n"; } public: operator const char * () const { return text.c_str(); } operator const std::string () const { return text; } const std::string toString () const { return text; } bool operator == (const VERB & other) const { return (*this).value == other.value; } bool operator != (const VERB & other) const { return ! ( (*this) == other); } // --- static const VERB GET; static const VERB POST; static const VERB PUT; static const VERB DELETE; }; const std::string VERB::theStrings[] = {"GET", "POST", "PUT", "DELETE"}; const VERB VERB::GET = VERB ( VERB::Verb::GET_ ); const VERB VERB::POST = VERB ( VERB::Verb::POST_ ); const VERB VERB::PUT = VERB ( VERB::Verb::PUT_ ); const VERB VERB::DELETE = VERB ( VERB::Verb::DELETE_ ); // end of file