Un moyen facile d’utiliser des variables de types enum sous forme de chaîne dans C?

Voici ce que j’essaie de faire:

typedef enum { ONE, TWO, THREE } Numbers; 

J’essaie d’écrire une fonction qui ferait un cas similaire à celui-ci:

 char num_str[10]; int process_numbers_str(Numbers num) { switch(num) { case ONE: case TWO: case THREE: { strcpy(num_str, num); //some way to get the symbolic constant name in here? } break; default: return 0; //no match return 1; } 

Au lieu de définir à chaque fois, est-il possible de le définir en utilisant la variable enum comme je tente de le faire ci-dessus?

Il n’y a pas de solution intégrée. Le plus simple est d’utiliser un tableau de caractères char* où la valeur int de l’énumération est indexée sur une chaîne contenant le nom descriptif de cette énumération. Si vous avez un enum épars (celui qui ne commence pas à 0 ou qui a des lacunes dans la numérotation) où certains des mappages int sont suffisamment élevés pour rendre un mappage basé sur un tableau peu pratique, vous pouvez utiliser une table de hachage.

 // Define your enumeration like this (in say numbers.h); ENUM_BEGIN( Numbers ) ENUM(ONE), ENUM(TWO), ENUM(FOUR) ENUM_END( Numbers ) // The macros are defined in a more fundamental .h file (say defs.h); #define ENUM_BEGIN(typ) enum typ { #define ENUM(nam) nam #define ENUM_END(typ) }; // Now in one and only one .c file, redefine the ENUM macros and reinclude // the numbers.h file to build a ssortingng table #undef ENUM_BEGIN #undef ENUM #undef ENUM_END #define ENUM_BEGIN(typ) const char * typ ## _name_table [] = { #define ENUM(nam) #nam #define ENUM_END(typ) }; #undef NUMBERS_H_INCLUDED // whatever you need to do to enable reinclusion #include "numbers.h" // Now you can do exactly what you want to do, with no retyping, and for any // number of enumerated types defined with the ENUM macro family // Your code follows; char num_str[10]; int process_numbers_str(Numbers num) { switch(num) { case ONE: case TWO: case THREE: { strcpy(num_str, Numbers_name_table[num]); // eg TWO -> "TWO" } break; default: return 0; //no match return 1; } // Sweet no ? After being frustrated by this for years, I finally came up // with this solution for my most recent project and plan to reuse the idea // forever 

La technique de Faire quelque chose à la fois un identifiant C et une chaîne? peut être utilisé ici.

Comme d’habitude avec ce genre de préprocesseur, l’écriture et la compréhension du préprocesseur peuvent être difficiles et inclure le passage de macros à d’autres macros et implique l’utilisation d’opérateurs # et ##, mais son utilisation est très simple. Je trouve ce style très utile pour les longs énumérations, où maintenir deux fois la même liste peut être très gênant.

Code usine – tapé une seule fois, généralement caché dans l’en-tête:

enumFactory.h:

 // expansion macro for enum value definition #define ENUM_VALUE(name,assign) name assign, // expansion macro for enum to ssortingng conversion #define ENUM_CASE(name,assign) case name: return #name; // expansion macro for ssortingng to enum conversion #define ENUM_STRCMP(name,assign) if (!strcmp(str,#name)) return name; /// declare the access function and define enum values #define DECLARE_ENUM(EnumType,ENUM_DEF) \ enum EnumType { \ ENUM_DEF(ENUM_VALUE) \ }; \ const char *GetSsortingng(EnumType dummy); \ EnumType Get##EnumType##Value(const char *ssortingng); \ /// define the access function names #define DEFINE_ENUM(EnumType,ENUM_DEF) \ const char *GetSsortingng(EnumType value) \ { \ switch(value) \ { \ ENUM_DEF(ENUM_CASE) \ default: return ""; /* handle input error */ \ } \ } \ EnumType Get##EnumType##Value(const char *str) \ { \ ENUM_DEF(ENUM_STRCMP) \ return (EnumType)0; /* handle input error */ \ } \ 

Usine utilisée

someEnum.h:

 #include "enumFactory.h" #define SOME_ENUM(XX) \ XX(FirstValue,) \ XX(SecondValue,) \ XX(SomeOtherValue,=50) \ XX(OneMoreValue,=100) \ DECLARE_ENUM(SomeEnum,SOME_ENUM) 

someEnum.cpp:

 #include "someEnum.h" DEFINE_ENUM(SomeEnum,SOME_ENUM) 

La technique peut être facilement étendue pour que les macros XX acceptent plus d’arguments, et vous pouvez également préparer plus de macros pour remplacer XX pour des besoins différents, semblables aux trois que j’ai fournis dans cet exemple.

Comparaison avec les macros X utilisant #include / #define / #undef

Bien que cela soit similaire aux X-Macros, d’autres ont mentionné, je pense que cette solution est plus élégante en ce sens qu’elle ne nécessite rien, ce qui vous permet de cacher plus de choses compliquées dans le fichier d’en-tête. est quelque chose que vous ne touchez pas du tout lorsque vous devez définir un nouveau code, par conséquent, une nouvelle définition enum est beaucoup plus courte et plus propre.

Il existe un moyen de le faire: utiliser les macros X () . Ces macros utilisent le préprocesseur C pour construire des énumérations, des tableaux et des blocs de code à partir d’une liste de données source. Vous devez seulement append de nouveaux éléments au #define contenant la macro X (). L’instruction switch se développera automatiquement.

Votre exemple peut être écrit comme suit:

  // Source data -- Enum, Ssortingng #define X_NUMBERS \ X(ONE, "one") \ X(TWO, "two") \ X(THREE, "three") ... // Use preprocessor to create the Enum typedef enum { #define X(Enum, Ssortingng) Enum, X_NUMBERS #undef X } Numbers; ... // Use Preprocessor to expand data into switch statement cases switch(num) { #define X(Enum, Ssortingng) \ case Enum: strcpy(num_str, Ssortingng); break; X_NUMBERS #undef X default: return 0; break; } return 1; 

Il existe des moyens plus efficaces (c.-à-d. Utiliser des macros X pour créer un tableau de chaînes et un index enum), mais c’est la démonstration la plus simple.

Je sais que vous avez deux bonnes réponses, mais connaissez-vous l’opérateur # dans le préprocesseur C?

Cela vous permet de faire ceci:

 #define MACROSTR(k) #k typedef enum { kZero, kOne, kTwo, kThree } kConst; static char *kConstStr[] = { MACROSTR(kZero), MACROSTR(kOne), MACROSTR(kTwo), MACROSTR(kThree) }; static void kConstPrinter(kConst k) { printf("%s", kConstStr[k]); } 

C ou C ++ ne fournit pas cette fonctionnalité, même si j’en ai souvent besoin.

Le code suivant fonctionne, même s’il convient mieux aux énumérations non éparses.

 typedef enum { ONE, TWO, THREE } Numbers; char *strNumbers[] = {"one","two","three"}; printf ("Value for TWO is %s\n",strNumbers[TWO]); 

Par non-clairsemé, je veux dire pas de la forme

 typedef enum { ONE, FOUR_THOUSAND = 4000 } Numbers; 

car cela comporte d’énormes lacunes.

L’avantage de cette méthode est qu’elle met les définitions des énumérations et des chaînes les unes à côté des autres. avoir une instruction switch dans une fonction les déclenche. Cela signifie que vous êtes moins susceptible de changer l’un sans l’autre.

BAISER. Vous ferez toutes sortes d’autres choses avec vos enums, alors pourquoi l’impression devrait-elle être différente? Oublier un cas dans votre routine d’impression n’est pas une grosse affaire quand vous considérez qu’il y a environ 100 autres endroits où vous pouvez oublier une affaire. Il suffit de comstackr -Wall, qui avertira des cas non exhaustifs. N’utilisez pas “default” car cela rendra le commutateur exhaustif et vous ne recevrez aucun avertissement. Au lieu de cela, laissez le commutateur sortir et gérez le cas par défaut comme si …

 const char *myenum_str(myenum e) { switch(e) { case ONE: return "one"; case TWO: return "two"; } return "invalid"; } 

Essayez de convertir les énumérations C ++ en chaînes . Les commentaires ont des améliorations qui résolvent le problème lorsque les éléments enum ont des valeurs arbitraires.

L’utilisation de boost :: preprocessor rend possible une solution élégante comme celle-ci:

Étape 1: inclure le fichier d’en-tête:

 #include "EnumUtilities.h" 

Étape 2: déclarez l’object d’énumération avec la syntaxe suivante:

 MakeEnum( TestData, (x) (y) (z) ); 

Étape 3: utilisez vos données:

Obtenir le nombre d’éléments:

 td::cout < < "Number of Elements: " << TestDataCount << std::endl; 

Obtenir la chaîne associée:

 std::cout < < "Value of " << TestData2String(x) << " is " << x << std::endl; std::cout << "Value of " << TestData2String(y) << " is " << y << std::endl; std::cout << "Value of " << TestData2String(z) << " is " << z << std::endl; 

Obtenir la valeur enum de la chaîne associée:

 std::cout < < "Value of x is " << TestData2Enum("x") << std::endl; std::cout << "Value of y is " << TestData2Enum("y") << std::endl; std::cout << "Value of z is " << TestData2Enum("z") << std::endl; 

Cela semble propre et compact, sans fichiers supplémentaires à inclure. Le code que j'ai écrit dans EnumUtilities.h est le suivant:

 #include  #include  #define REALLY_MAKE_STRING(x) #x #define MAKE_STRING(x) REALLY_MAKE_STRING(x) #define MACRO1(r, data, elem) elem, #define MACRO1_STRING(r, data, elem) case elem: return REALLY_MAKE_STRING(elem); #define MACRO1_ENUM(r, data, elem) if (REALLY_MAKE_STRING(elem) == eStrEl) return elem; #define MakeEnum(eName, SEQ) \ enum eName { BOOST_PP_SEQ_FOR_EACH(MACRO1, , SEQ) \ last_##eName##_enum}; \ const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \ static std::ssortingng eName##2Ssortingng(const enum eName eel) \ { \ switch (eel) \ { \ BOOST_PP_SEQ_FOR_EACH(MACRO1_STRING, , SEQ) \ default: return "Unknown enumerator value."; \ }; \ }; \ static enum eName eName##2Enum(const std::ssortingng eStrEl) \ { \ BOOST_PP_SEQ_FOR_EACH(MACRO1_ENUM, , SEQ) \ return (enum eName)0; \ }; 

Il existe certaines limitations, à savoir celles de boost :: preprocessor. Dans ce cas, la liste des constantes ne peut pas dépasser 64 éléments.

Suivant la même logique, vous pouvez aussi penser à créer un enum épars:

 #define EnumName(Tuple) BOOST_PP_TUPLE_ELEM(2, 0, Tuple) #define EnumValue(Tuple) BOOST_PP_TUPLE_ELEM(2, 1, Tuple) #define MACRO2(r, data, elem) EnumName(elem) EnumValue(elem), #define MACRO2_STRING(r, data, elem) case EnumName(elem): return BOOST_PP_STRINGIZE(EnumName(elem)); #define MakeEnumEx(eName, SEQ) \ enum eName { \ BOOST_PP_SEQ_FOR_EACH(MACRO2, _, SEQ) \ last_##eName##_enum }; \ const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \ static std::ssortingng eName##2Ssortingng(const enum eName eel) \ { \ switch (eel) \ { \ BOOST_PP_SEQ_FOR_EACH(MACRO2_STRING, _, SEQ) \ default: return "Unknown enumerator value."; \ }; \ }; 

Dans ce cas, la syntaxe est la suivante:

 MakeEnumEx(TestEnum, ((x,)) ((y,=1000)) ((z,)) ); 

L'utilisation est similaire à celle ci-dessus (moins la fonction eName ## 2Enum, que vous pourriez essayer d'extrapoler à partir de la syntaxe précédente).

Je l'ai testé sur Mac et Linux, mais sachez que boost :: preprocessor n'est peut-être pas totalement portable.

En fusionnant certaines des techniques ici, j’ai trouvé la forme la plus simple:

 #define MACROSTR(k) #k #define X_NUMBERS \ X(kZero ) \ X(kOne ) \ X(kTwo ) \ X(kThree ) \ X(kFour ) \ X(kMax ) enum { #define X(Enum) Enum, X_NUMBERS #undef X } kConst; static char *kConstStr[] = { #define X(Ssortingng) MACROSTR(Ssortingng), X_NUMBERS #undef X }; int main(void) { int k; printf("Hello World!\n\n"); for (k = 0; k < kMax; k++) { printf("%s\n", kConstStr[k]); } return 0; } 

Si vous utilisez gcc, il est possible d’utiliser:

 const char * enum_to_ssortingng_map[]={ [enum1]='ssortingng1', [enum2]='ssortingng2'}; 

Ensuite, appelez simplement par exemple

 enum_to_ssortingng_map[enum1] 

Découvrez les idées sur Mu Dynamics Research Labs – Blog Archive . Je l’ai trouvé plus tôt cette année – j’ai oublié le contexte exact où je l’ai trouvé – et je l’ai adapté à ce code. Nous pouvons débattre des avantages de l’ajout d’un E à l’avant; il est applicable au problème spécifique abordé, mais ne fait pas partie d’une solution générale. Je l’ai caché dans mon dossier ‘vignettes’ – où je garde des bouts de code intéressants au cas où je les souhaiterais plus tard. Je suis gêné de dire que je n’ai pas noté l’origine de cette idée à l’époque.

En-tête: paste1.h

 /* @(#)File: $RCSfile: paste1.h,v $ @(#)Version: $Revision: 1.1 $ @(#)Last changed: $Date: 2008/05/17 21:38:05 $ @(#)Purpose: Automated Token Pasting */ #ifndef JLSS_ID_PASTE_H #define JLSS_ID_PASTE_H /* * Common case when someone just includes this file. In this case, * they just get the various E* tokens as good old enums. */ #if !defined(ETYPE) #define ETYPE(val, desc) E##val, #define ETYPE_ENUM enum { #endif /* ETYPE */ ETYPE(PERM, "Operation not permitted") ETYPE(NOENT, "No such file or directory") ETYPE(SRCH, "No such process") ETYPE(INTR, "Interrupted system call") ETYPE(IO, "I/O error") ETYPE(NXIO, "No such device or address") ETYPE(2BIG, "Arg list too long") /* * Close up the enum block in the common case of someone including * this file. */ #if defined(ETYPE_ENUM) #undef ETYPE_ENUM #undef ETYPE ETYPE_MAX }; #endif /* ETYPE_ENUM */ #endif /* JLSS_ID_PASTE_H */ 

Exemple de source:

 /* @(#)File: $RCSfile: paste1.c,v $ @(#)Version: $Revision: 1.2 $ @(#)Last changed: $Date: 2008/06/24 01:03:38 $ @(#)Purpose: Automated Token Pasting */ #include "paste1.h" static const char *sys_errlist_internal[] = { #undef JLSS_ID_PASTE_H #define ETYPE(val, desc) desc, #include "paste1.h" 0 #undef ETYPE }; static const char *xerror(int err) { if (err >= ETYPE_MAX || err < = 0) return "Unknown error"; return sys_errlist_internal[err]; } static const char*errlist_mnemonics[] = { #undef JLSS_ID_PASTE_H #define ETYPE(val, desc) [E ## val] = "E" #val, #include "paste1.h" #undef ETYPE }; #include  int main(void) { int i; for (i = 0; i < ETYPE_MAX; i++) { printf("%d: %-6s: %s\n", i, errlist_mnemonics[i], xerror(i)); } return(0); } 

Pas nécessairement l'utilisation la plus propre du pré-processeur C au monde - mais cela empêche l'écriture du matériel à plusieurs resockets.

Faire quelque chose à la fois un identifiant C et une chaîne

Si l’index d’énumération est basé sur 0, vous pouvez mettre les noms dans un tableau de char * et les indexer avec la valeur enum.

 #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

J’ai créé une classe streamable_enum simple streamable_enum qui utilise les opérateurs de stream < < et >> et est basée sur le std::map :

 #ifndef STREAMABLE_ENUM_HPP #define STREAMABLE_ENUM_HPP #include  #include  #include  template  class streamable_enum { public: typedef typename std::map tostr_map_t; typedef typename std::map fromstr_map_t; streamable_enum() {} streamable_enum(E val) : Val_(val) {} operator E() { return Val_; } bool operator==(const streamable_enum& e) { return this->Val_ == e.Val_; } bool operator==(const E& e) { return this->Val_ == e; } static const tostr_map_t& to_ssortingng_map() { static tostr_map_t to_str_(get_enum_ssortingngs()); return to_str_; } static const fromstr_map_t& from_ssortingng_map() { static fromstr_map_t from_str_(reverse_map(to_ssortingng_map())); return from_str_; } private: E Val_; static fromstr_map_t reverse_map(const tostr_map_t& eToS) { fromstr_map_t sToE; for (auto pr : eToS) { sToE.emplace(pr.second, pr.first); } return sToE; } }; template  streamable_enum stream_enum(E e) { return streamable_enum(e); } template  typename streamable_enum::tostr_map_t get_enum_ssortingngs() { // \todo throw an appropriate exception or display comstack error/warning return {}; } template  std::ostream& operator< <(std::ostream& os, streamable_enum e) { auto& mp = streamable_enum::to_ssortingng_map(); auto res = mp.find(e); if (res != mp.end()) { os < < res->second; } else { os.setstate(std::ios_base::failbit); } return os; } template  std::istream& operator>>(std::istream& is, streamable_enum& e) { std::ssortingng str; is >> str; if (str.empty()) { is.setstate(std::ios_base::failbit); } auto& mp = streamable_enum::from_ssortingng_map(); auto res = mp.find(str); if (res != mp.end()) { e = res->second; } else { is.setstate(std::ios_base::failbit); } return is; } #endif 

Usage:

 #include "streamable_enum.hpp" using std::cout; using std::cin; using std::endl; enum Animal { CAT, DOG, TIGER, RABBIT }; template <> streamable_enum::tostr_map_t get_enum_ssortingngs() { return { { CAT, "Cat"}, { DOG, "Dog" }, { TIGER, "Tiger" }, { RABBIT, "Rabbit" } }; } int main(int argc, char* argv []) { cout < < "What animal do you want to buy? Our offering:" << endl; for (auto pr : streamable_enum::to_ssortingng_map()) { // Use from_ssortingng_map() and pr.first instead cout < < " " << pr.second << endl; // to have them sorted in alphabetical order } streamable_enum anim; cin >> anim; if (!cin) { cout < < "We don't have such animal here." << endl; } else if (anim == Animal::TIGER) { cout << stream_enum(Animal::TIGER) << " was a joke..." << endl; } else { cout << "Here you are!" << endl; } return 0; } 

Voici une solution utilisant des macros avec les fonctionnalités suivantes:

  1. n’écris qu’une seule fois chaque valeur de l’énumération, il n’y a donc pas de double liste à maintenir

  2. ne conservez pas les valeurs d’énumération dans un fichier séparé qui sera plus tard inclus afin que je puisse l’écrire où je veux

  3. ne remplacez pas l’enum lui-même, je veux toujours avoir le type enum défini, mais en plus je veux pouvoir mapper chaque nom d’énumération avec la chaîne correspondante (pour ne pas affecter le code hérité)

  4. la recherche devrait être rapide, donc de préférence pas de casier, pour ces énormes enums

https://stackoverflow.com/a/20134475/1812866

Je pensais qu’une solution comme Boost.Fusion pour adapter des structures et des classes serait bien, ils l’avaient même à un moment donné, d’utiliser des enums comme une séquence de fusion.

J’ai donc fait juste quelques petites macros pour générer le code pour imprimer les énumérations. Ce n’est pas parfait et n’a rien à voir avec Boost. Un code passe-partout généré, mais peut être utilisé comme les macros Boost Fusion. Je veux vraiment générer les types requirejs par Boost.Fusion pour les intégrer dans cette infrastructure qui permet d’imprimer les noms des membres de la structure, mais cela se produira plus tard, pour l’instant ce ne sont que des macros:

 #ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP #define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP #include  #include  #include  #include  #include  #include  #define SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C( \ R, unused, ENUMERATION_ENTRY) \ case ENUMERATION_ENTRY: \ return BOOST_PP_STRINGIZE(ENUMERATION_ENTRY); \ break; /** * \brief Adapts ENUM to reflectable types. * * \param ENUM_TYPE To be adapted * \param ENUMERATION_SEQ Sequence of enum states */ #define SWISSARMYKNIFE_ADAPT_ENUM(ENUM_TYPE, ENUMERATION_SEQ) \ inline std::ssortingng to_ssortingng(const ENUM_TYPE& enum_value) { \ switch (enum_value) { \ BOOST_PP_SEQ_FOR_EACH( \ SWISSARMYKNIFE_ADAPT_ENUM_EACH_ENUMERATION_ENTRY_C, \ unused, ENUMERATION_SEQ) \ default: \ return BOOST_PP_STRINGIZE(ENUM_TYPE); \ } \ } \ \ inline std::ostream& operator< <(std::ostream& os, const ENUM_TYPE& value) { \ os << to_string(value); \ return os; \ } #endif 

L'ancienne réponse ci-dessous est plutôt mauvaise, veuillez ne pas l'utiliser. :)

Vieille réponse:

J'ai cherché un moyen de résoudre ce problème sans trop modifier la syntaxe de la déclaration enums. Je suis arrivé à une solution qui utilise le préprocesseur pour récupérer une chaîne à partir d’une déclaration enum.

Je suis capable de définir des énumérations non clairsemées comme ceci:

 SMART_ENUM(State, enum State { RUNNING, SLEEPING, FAULT, UNKNOWN }) 

Et je peux interagir avec eux de différentes manières:

 // With a ssortingngstream std::ssortingngstream ss; ss < < State::FAULT; std::string myEnumStr = ss.str(); //Directly to stdout std::cout << State::FAULT << std::endl; //to a string std::string myStr = State::to_string(State::FAULT); //from a string State::State myEnumVal = State::from_string(State::FAULT); 

Basé sur les définitions suivantes:

 #define SMART_ENUM(enumTypeArg, ...) \ namespace enumTypeArg { \ __VA_ARGS__; \ std::ostream& operator< <(std::ostream& os, const enumTypeArg& val) { \ os << swissarmyknife::enums::to_string(#__VA_ARGS__, val); \ return os; \ } \ \ std::string to_string(const enumTypeArg& val) { \ return swissarmyknife::enums::to_string(#__VA_ARGS__, val); \ } \ \ enumTypeArg from_string(const std::string &str) { \ return swissarmyknife::enums::from_string(#__VA_ARGS__, str); \ } \ } \ namespace swissarmyknife { namespace enums { static inline std::ssortingng to_ssortingng(const std::ssortingng completeEnumDeclaration, size_t enumVal) throw (std::runtime_error) { size_t begin = completeEnumDeclaration.find_first_of('{'); size_t end = completeEnumDeclaration.find_last_of('}'); const std::ssortingng identifiers = completeEnumDeclaration.substr(begin + 1, end ); size_t count = 0; size_t found = 0; do { found = identifiers.find_first_of(",}", found+1); if (enumVal == count) { std::ssortingng identifiersSubset = identifiers.substr(0, found); size_t beginId = identifiersSubset.find_last_of("{,"); identifiersSubset = identifiersSubset.substr(beginId+1); boost::algorithm::sortingm(identifiersSubset); return identifiersSubset; } ++count; } while (found != std::ssortingng::npos); throw std::runtime_error("The enum declaration provided doesn't contains this state."); } template  static inline EnumType from_ssortingng(const std::ssortingng completeEnumDeclaration, const std::ssortingng &enumStr) throw (std::runtime_error) { size_t begin = completeEnumDeclaration.find_first_of('{'); size_t end = completeEnumDeclaration.find_last_of('}'); const std::ssortingng identifiers = completeEnumDeclaration.substr(begin + 1, end ); size_t count = 0; size_t found = 0; do { found = identifiers.find_first_of(",}", found+1); std::ssortingng identifiersSubset = identifiers.substr(0, found); size_t beginId = identifiersSubset.find_last_of("{,"); identifiersSubset = identifiersSubset.substr(beginId+1); boost::algorithm::sortingm(identifiersSubset); if (identifiersSubset == enumStr) { return static_cast(count); } ++count; } while (found != std::ssortingng::npos); throw std::runtime_error("No valid enum value for the provided ssortingng"); } }} 

Quand j'aurai besoin de support pour les énumérations éparses et quand j'aurai plus de temps, j'améliorerai les implémentations de to_ssortingng et from_ssortingng avec boost :: xpressive, mais cela coûtera du temps de compilation en raison de l'importante modélisation effectuée et de l'exécutable généré. susceptible d'être vraiment plus grand. Mais cela présente l'avantage d'être plus lisible et maintenable que ce code de manipulation manuel des chaînes. :RÉ

Sinon, j'ai toujours utilisé boost :: bimap pour effectuer de telles correspondances entre la valeur et la chaîne, mais il doit être maintenu manuellement.