Comment mapper facilement des énumérations C ++ à des chaînes

119

J'ai un tas de types d'énumérations dans certains fichiers d'en-tête de bibliothèque que j'utilise, et je veux avoir un moyen de convertir les valeurs d'énumération en chaînes utilisateur - et vice-versa.

RTTI ne le fera pas pour moi, car les «chaînes utilisateur» doivent être un peu plus lisibles que les énumérations.

Une solution de force brute serait un tas de fonctions comme celle-ci, mais je pense que c'est un peu trop C-like.

enum MyEnum {VAL1, VAL2,VAL3};

String getStringFromEnum(MyEnum e)
{
  switch e
  {
  case VAL1: return "Value 1";
  case VAL2: return "Value 2";
  case VAL1: return "Value 3";
  default: throw Exception("Bad MyEnum");
  }
}

J'ai le sentiment instinctif qu'il existe une solution élégante utilisant des modèles, mais je ne peux pas encore tout à fait comprendre.

MISE À JOUR: Merci pour vos suggestions - j'aurais dû préciser que les énumérations sont définies dans un en-tête de bibliothèque tiers, donc je ne veux pas avoir à en changer la définition.

Mon instinct est maintenant d'éviter les modèles et de faire quelque chose comme ceci:

char * MyGetValue(int v, char *tmp); // implementation is trivial

#define ENUM_MAP(type, strings) char * getStringValue(const type &T) \
 { \
 return MyGetValue((int)T, strings); \
 }

; enum eee {AA,BB,CC}; - exists in library header file 
; enum fff {DD,GG,HH}; 

ENUM_MAP(eee,"AA|BB|CC")
ENUM_MAP(fff,"DD|GG|HH")

// To use...

    eee e;
    fff f;
    std::cout<< getStringValue(e);
    std::cout<< getStringValue(f);
Roddy
la source
1
Double possible de Y a
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

60

Si vous voulez que les noms d'énumérations eux-mêmes soient des chaînes, consultez cet article . Sinon, ça std::map<MyEnum, char const*>fonctionnera bien. (Inutile de copier vos littéraux de chaîne dans std :: strings dans la carte)

Pour plus de sucre syntaxique, voici comment écrire une classe map_init. Le but est de permettre

std::map<MyEnum, const char*> MyMap;
map_init(MyMap)
    (eValue1, "A")
    (eValue2, "B")
    (eValue3, "C")
;

La fonction template <typename T> map_init(T&)renvoie un map_init_helper<T>. map_init_helper<T>stocke un T &, et définit le trivial map_init_helper& operator()(typename T::key_type const&, typename T::value_type const&). (Le retour *thisde operator()permet le chaînage de operator(), comme operator<<sur std::ostreams)

template<typename T> struct map_init_helper
{
    T& data;
    map_init_helper(T& d) : data(d) {}
    map_init_helper& operator() (typename T::key_type const& key, typename T::mapped_type const& value)
    {
        data[key] = value;
        return *this;
    }
};

template<typename T> map_init_helper<T> map_init(T& item)
{
    return map_init_helper<T>(item);
}

Étant donné que la fonction et la classe d'assistance sont basées sur un modèle, vous pouvez les utiliser pour n'importe quelle carte ou structure de type carte. Ie il peut également ajouter des entrées àstd::unordered_map

Si vous n'aimez pas écrire ces helpers, boost :: assign offre la même fonctionnalité prête à l'emploi.

MSalters
la source
Vous avez raison de vous référer à une autre question. Les gens devraient jeter un oeil dans les "questions connexes" avant de poster ...
xtofl
2
@xtofl: Les «questions connexes» présentées ici sont totalement différentes des questions connexes répertoriées lorsque j'ai posté la question!
Roddy
@MSalters, un std :: map est un moyen utile de gérer l'implémentation, mais je cherche des moyens de réduire le code standard qui pourrait nécessiter.
Roddy
@MSalters, ce serait bien de pouvoir accepter plusieurs arguments pour l'opérateur []. mais malheureusement, on ne peut pas faire cela. x [a, b] s'évalue à x [b]. l'expression (a, b) utilise l'opérateur virgule. il est donc équivalent à ["A"] ["B"] ["C"] dans votre code. vous pouvez le changer pour dire [eValue1] ["A"] [eValu ..
Johannes Schaub - litb
l'opérateur d'appel de fonction serait aussi un bon candidat: map_init (MyMap) (eValue1, "A") (eValue2, "B") .... alors c'est équivalent à boost :: assign: insert (MyMap) (eValue1, "A") (eValue2, "B") ... ( boost.org/doc/libs/1_35_0/libs/assign/doc/index.html )
Johannes Schaub - litb
31

La solution MSalters est bonne, mais elle se réinstalle essentiellement boost::assign::map_list_of. Si vous avez boost, vous pouvez l'utiliser directement:

#include <boost/assign/list_of.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>

using boost::assign::map_list_of;

enum eee { AA,BB,CC };

const boost::unordered_map<eee,const char*> eeeToString = map_list_of
    (AA, "AA")
    (BB, "BB")
    (CC, "CC");

int main()
{
    std::cout << " enum AA = " << eeeToString.at(AA) << std::endl;
    return 0;
}
Alastair
la source
Comment utiliseriez-vous cela là où eeeToString est un membre de données d'une classe? J'obtiens "Erreur: l'initialisation des membres de données n'est pas autorisée"
Utilisateur
@User: les membres de données de classe sont initialisés dans les constructeurs, généralement dans la liste d'initialisation.
MSalters
Existe-t-il un moyen de faire en sorte que cela fonctionne pour toutes les énumérations. J'ai plusieurs déclarations d'énumération et je ne veux pas que la carte fonctionne uniquement pour le type eeedans votre cas.
Justin Liang
J'ai essayé d' utiliser un modèle mais reçu et erreur: error: template declaration of 'const boost::unordered::unordered_map<T, const char*> enumToString'.
Justin Liang
4
En fait, cette réponse est largement obsolète avec C ++ 11.
Alastair
19

Générer automatiquement un formulaire à partir d'un autre.

La source:

enum {
  VALUE1, /* value 1 */
  VALUE2, /* value 2 */
};

Généré:

const char* enum2str[] = {
  "value 1", /* VALUE1 */
  "value 2", /* VALUE2 */
};

Si les valeurs d'énumération sont grandes, un formulaire généré pourrait utiliser unordered_map <> ou des modèles comme suggéré par Constantin.

La source:

enum State{
  state0 = 0, /* state 0 */
  state1 = 1, /* state 1 */
  state2 = 2, /* state 2 */
  state3 = 4, /* state 3 */

  state16 = 0x10000, /* state 16 */
};

Généré:

template <State n> struct enum2str { static const char * const value; };
template <State n> const char * const enum2str<n>::value = "error";

template <> struct enum2str<state0> { static const char * const value; };
const char * const enum2str<state0>::value = "state 0";

Exemple:

#include <iostream>

int main()
{
  std::cout << enum2str<state16>::value << std::endl;
  return 0;
}
jfs
la source
Bien que plus rapide, ce n'est pas aussi simple que @MSalters.
kenny
2
C'est si vous avez un peu de perl / python pour lire une liste de chaînes à partir d'un fichier texte et générer un fichier .h avec le caractère statique au moment de la compilation. = "Ecrire des programmes pour écrire des programmes"
Martin Beckett
@mgb: perl / python ne sont pas les seules options que presque n'importe quel moteur de modèle dans n'importe quel langage fera (dans ce cas, on génère les deux formulaires à partir d'un modèle).
jfs
@jf. Oui, le point important était de créer automatiquement des tables de données statiques au moment de la compilation. Je préférerais probablement simplement générer un tableau statique stupide.
Martin Beckett
Cela fonctionnera-t-il si l'état n'est pas connu au moment de la compilation? Je suis à peu près sûr que ce ne sera pas le cas - en théorie, le compilateur devrait instancier le modèle enum2str avec toutes les valeurs possibles de l'énumération, ce que je suis presque sûr que gcc (au moins) ne fera pas.
Alastair
11

Je me souviens avoir répondu à cela ailleurs sur StackOverflow. Répétez-le ici. En gros, c'est une solution basée sur des macros variadiques, et est assez facile à utiliser:

#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<std::string> strings; \
std::ostringstream 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<int>(value)]; \
return os;} 

Pour l'utiliser dans votre code, faites simplement:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);
auto dog = Animal::DOG;
std::cout<<dog;
Debdatta Basu
la source
1
Changez simplement la déclaration de classe enum en enum pour qu'elle fonctionne sur des versions antérieures à C ++ 11.
Debdatta Basu
1
Vous avez raison, cela fonctionne (l'auto est également uniquement C ++ 11). Belle solution! Ce serait parfait si vous pouviez également définir une valeur pour certains
énumérations
Je suppose que j'ai vu dans boost quelque chose comme ça
Sergei
10

Je suggère qu'un mélange d'utilisation de X-macros est la meilleure solution et les fonctions de modèle suivantes:

Pour emprunter marcinkoziukmyopenidcom et prolongé

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
};

template <class T> T str2enum( const char* );
template <class T> const char* enum2str( T );

#define STR2ENUM(TYPE,ARRAY) \
template <> \
TYPE str2enum<TYPE>( const char* str ) \
    { \
    for( int i = 0; i < (sizeof(ARRAY)/sizeof(ARRAY[0])); i++ ) \
        if( !strcmp( ARRAY[i], str ) ) \
            return TYPE(i); \
    return TYPE(0); \
    }

#define ENUM2STR(TYPE,ARRAY) \
template <> \
const char* enum2str<TYPE>( TYPE v ) \
    { \
    return ARRAY[v]; \
    }

#define ENUMANDSTR(TYPE,ARRAY)\
    STR2ENUM(TYPE,ARRAY) \
    ENUM2STR(TYPE,ARRAY)

ENUMANDSTR(Colours,colours_str)

colour.def

X(Red)
X(Green)
X(Blue)
X(Cyan)
X(Yellow)
X(Magenta)
David Allan Finch
la source
Existe-t-il un moyen de rendre générique la définition du tableau de chaînes enum? (Je ne sais pas comment gérer une X-Macro dans une macro et je ne gère pas facilement le modèle)
Jonathan
5

J'utilise cette solution que je reproduis ci-dessous:

#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(String) MACROSTR(String),
    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;
}
Juan Gonzalez Burgos
la source
1
Ce sont des macros X basiques, et je suis stupéfait que ce soit la première réponse ici pour le suggérer! +1
Courses de légèreté en orbite
4

Si vous souhaitez obtenir des représentations sous forme de chaîne de MyEnum variables , les modèles ne le couperont pas. Le modèle peut être spécialisé sur les valeurs intégrales connues au moment de la compilation.

Cependant, si c'est ce que vous voulez, essayez:

#include <iostream>

enum MyEnum { VAL1, VAL2 };

template<MyEnum n> struct StrMyEnum {
    static char const* name() { return "Unknown"; }
};

#define STRENUM(val, str) \
  template<> struct StrMyEnum<val> { \
    static char const* name() { return str; }};

STRENUM(VAL1, "Value 1");
STRENUM(VAL2, "Value 2");

int main() {
  std::cout << StrMyEnum<VAL2>::name();
}

C'est verbeux, mais attrapera des erreurs comme celle que vous avez faite en question - votre case VAL1 est dupliqué.

Constantin
la source
En fait, le nom de la méthode () n'est pas nécessaire. Voyez ma réponse.
jfs
3

J'ai passé plus de temps à faire des recherches sur ce sujet que j'aimerais admettre. Heureusement, il existe d'excellentes solutions open source dans la nature.

Ce sont deux grandes approches, même si elles ne sont pas (encore) assez connues,

sage_enum

  • Bibliothèque d'énumération intelligente autonome pour C ++ 11/14/17. Il prend en charge toutes les fonctionnalités standard que vous attendez d'une classe d'énumération intelligente en C ++.
  • Limitations: nécessite au moins C ++ 11.

Meilleurs énumérations

  • Bibliothèque d'énumération réfléchissante au moment de la compilation avec une syntaxe propre, dans un seul fichier d'en-tête et sans dépendances.
  • Limitations: basé sur des macros, ne peut pas être utilisé dans une classe.
jose.angel.jimenez
la source
2

Je serais tenté d'avoir une carte m - et l'incorporer dans l'énumération.

setup avec m [MyEnum.VAL1] = "Valeur 1";

et tout est fait.

Richard Harrison
la source
2

J'ai eu besoin de cette fonctionnalité plusieurs fois pour déboguer / analyser le code des autres. Pour cela, j'ai écrit un script Perl qui génère une classe avec plusieurs toStringméthodes surchargées . Chaque toStringméthode prend un Enumcomme argument et retourne const char*.

Bien sûr, le script n'analyse pas C ++ pour les énumérations lui-même, mais utilise des ctags pour générer une table de symboles.

Le script Perl est ici: http://heinitz-it.de/download/enum2string/enum2string.pl.html

Valentin Heinitz
la source
2

Vos réponses m'ont inspiré à écrire moi-même des macros. Mes exigences étaient les suivantes:

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

  2. ne gardez 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'énumération elle-même, je veux toujours que le type enum soit défini, mais en plus de cela, je veux pouvoir mapper chaque nom enum à la chaîne correspondante (pour ne pas affecter le code hérité)

  4. la recherche doit être rapide, donc de préférence pas de boîtier de commutation, pour ces énormes énumérations

Ce code crée une énumération classique avec certaines valeurs. De plus, il crée comme std :: map qui mappe chaque valeur d'énumération à son nom (ie map [E_SUNDAY] = "E_SUNDAY", etc.)

Ok, voici le code maintenant:

EnumUtilsImpl.h :

map<int, string> & operator , (map<int, string> & dest, 
                               const pair<int, string> & keyValue) {
    dest[keyValue.first] = keyValue.second; 
    return dest;
}

#define ADD_TO_MAP(name, value) pair<int, string>(name, #name)

EnumUtils.h // c'est le fichier que vous voulez inclure chaque fois que vous aurez besoin de faire cela, vous utiliserez les macros de celui-ci:

#include "EnumUtilsImpl.h"
#define ADD_TO_ENUM(name, value) \
    name value

#define MAKE_ENUM_MAP_GLOBAL(values, mapName) \
    int __makeMap##mapName() {mapName, values(ADD_TO_MAP); return 0;}  \
    int __makeMapTmp##mapName = __makeMap##mapName();

#define MAKE_ENUM_MAP(values, mapName) \
    mapName, values(ADD_TO_MAP);

MyProjectCodeFile.h // ceci est un exemple de la façon de l'utiliser pour créer une énumération personnalisée:

#include "EnumUtils.h*

#define MyEnumValues(ADD) \
    ADD(val1, ), \
    ADD(val2, ), \
    ADD(val3, = 100), \
    ADD(val4, )

enum MyEnum {
    MyEnumValues(ADD_TO_ENUM)
};

map<int, string> MyEnumStrings;
// this is how you initialize it outside any function
MAKE_ENUM_MAP_GLOBAL(MyEnumValues, MyEnumStrings); 

void MyInitializationMethod()
{ 
    // or you can initialize it inside one of your functions/methods
    MAKE_ENUM_MAP(MyEnumValues, MyEnumStrings); 
}

À votre santé.

muqker
la source
2

Voici une tentative pour obtenir automatiquement les opérateurs de flux << et >> sur enum avec une commande de macro d'une seule ligne ...

Définitions:

#include <string>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>

#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_(attribute, name, ...) name { __VA_ARGS__ }; \
    attribute std::istream& operator>>(std::istream& is, name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        std::string str; \
        std::istream& r = is >> str; \
        const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
        const std::vector<std::string> enumStr(name##Str, name##Str + len); \
        const std::vector<std::string>::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; \
    }; \
    attribute 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;
}

Je ne suis pas sûr des limites de ce système ... les commentaires sont les bienvenus!

OlivierB
la source
1

dans l'en-tête:

enum EFooOptions
 {
FooOptionsA = 0, EFooOptionsMin = 0,
FooOptionsB,
FooOptionsC,
FooOptionsD 
EFooOptionsMax
};
extern const wchar* FOO_OPTIONS[EFooOptionsMax];

dans le fichier .cpp:

const wchar* FOO_OPTIONS[] = {
    L"One",
    L"Two",
    L"Three",
    L"Four"
};

Avertissement: ne gérez pas un mauvais index de tableau. :) Mais vous pouvez facilement ajouter une fonction pour vérifier l'énumération avant d'obtenir la chaîne du tableau.

moogs
la source
En effet une solution très non-DRY-SPOT.
xtofl
maintenant que vous mentionnez DRY. les fichiers .h et .cpp générés automatiquement à partir d'un autre fichier d'entrée. J'aimerais voir de meilleures solutions (qui ne recourent pas à une complexité inutile)
Moogs
1

Je voulais juste montrer cette solution élégante possible à l'aide de macros. Cela ne résout pas le problème, mais je pense que c'est un bon moyen de repenser le problème.

#define MY_LIST(X) X(value1), X(value2), X(value3)

enum eMyEnum
    {
    MY_LIST(PLAIN)
    };

const char *szMyEnum[] =
    {
    MY_LIST(STRINGY)
    };


int main(int argc, char *argv[])
{

std::cout << szMyEnum[value1] << value1 <<" " <<  szMyEnum[value2] << value2 << std::endl;

return 0;
}

---- ÉDITER ----

Après quelques recherches sur Internet et quelques expériences personnelles, je suis arrivé à la solution suivante:

//this is the enum definition
#define COLOR_LIST(X) \
  X( RED    ,=21)      \
  X( GREEN  )      \
  X( BLUE   )      \
  X( PURPLE , =242)      \
  X( ORANGE )      \
  X( YELLOW )

//these are the macros
#define enumfunc(enums,value) enums,
#define enumfunc2(enums,value) enums value,
#define ENUM2SWITCHCASE(enums) case(enums): return #enums;

#define AUTOENUM(enumname,listname) enum enumname{listname(enumfunc2)};
#define ENUM2STRTABLE(funname,listname) char* funname(int val) {switch(val) {listname(ENUM2SWITCHCASE) default: return "undef";}}
#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int values[] = {listname(enumfunc)};int N = sizeof(values)/sizeof(int);ENUM2STRTABLE(enum2str,listname)};

//here the enum and the string enum map table are generated
AUTOENUM(testenum,COLOR_LIST)
ENUM2STRTABLE(testfunenum,COLOR_LIST)
ENUM2STRUCTINFO(colorinfo,COLOR_LIST)//colorinfo structur {int values[]; int N; char * enum2str(int);}

//debug macros
#define str(a) #a
#define xstr(a) str(a)


int main( int argc, char** argv )
{
testenum x = YELLOW;
std::cout << testfunenum(GREEN) << "   " << testfunenum(PURPLE) << PURPLE << "  " << testfunenum(x);

for (int i=0;i< colorinfo::N;i++)
std::cout << std::endl << colorinfo::values[i] <<  "  "<< colorinfo::enum2str(colorinfo::values[i]);

  return EXIT_SUCCESS;
}

Je voulais juste le poster, peut-être que quelqu'un pourrait trouver cette solution utile. Il n'y a pas besoin de classes de modèles, pas besoin de c ++ 11 et pas besoin de boost donc cela pourrait également être utilisé pour de simples C.

---- EDIT2 ----

la table d'informations peut produire des problèmes lors de l'utilisation de plus de 2 énumérations (problème du compilateur). La solution de contournement suivante a fonctionné:

#define ENUM2STRUCTINFO(spacename,listname) namespace spacename { int spacename##_##values[] = {listname(enumfunc)};int spacename##_##N = sizeof(spacename##_##values)/sizeof(int);ENUM2STRTABLE(spacename##_##enum2str,listname)};
jamk
la source
1
typedef enum {
    ERR_CODE_OK = 0,
    ERR_CODE_SNAP,

    ERR_CODE_NUM
} ERR_CODE;

const char* g_err_msg[ERR_CODE_NUM] = {
    /* ERR_CODE_OK   */ "OK",
    /* ERR_CODE_SNAP */ "Oh, snap!",
};

Ci-dessus, ma solution simple. Un avantage de celui-ci est le «NUM» qui contrôle la taille du tableau de messages, il empêche également l'accès hors des limites (si vous l'utilisez à bon escient).

Vous pouvez également définir une fonction pour obtenir la chaîne:

const char* get_err_msg(ERR_CODE code) {
    return g_err_msg[code];
}

Suite à ma solution, j'ai alors trouvé la suivante assez intéressante. Il a généralement résolu le problème de synchronisation de celui ci-dessus.

Diapositives ici: http://www.slideshare.net/arunksaha/touchless-enum-tostring-28684724

Code ici: https://github.com/arunksaha/enum_to_string

Madwyn
la source
1

Je sais que je suis en retard pour faire la fête, mais pour tous les autres qui viennent visiter cette page, vous pouvez essayer ceci, c'est plus facile que tout et cela a plus de sens:

namespace texs {
    typedef std::string Type;
    Type apple = "apple";
    Type wood = "wood";
}
uIM7AI9S
la source
Proposez-vous d'utiliser des chaînes et de ne pas utiliser du tout d'énumérations? Cela ne résout pas vraiment le problème.
Roddy
0

J'ai récemment eu le même problème avec une bibliothèque fournisseur (Fincad). Heureusement, le fournisseur a fourni une documentation XML pour toutes les énumérations. J'ai fini par générer une carte pour chaque type d'énumération et fournir une fonction de recherche pour chaque énumération. Cette technique vous permet également d'intercepter une recherche en dehors de la plage de l'énumération.

Je suis sûr que swig pourrait faire quelque chose de similaire pour vous, mais je suis heureux de fournir les utilitaires de génération de code qui sont écrits en ruby.

Voici un exemple de code:

std::map<std::string, switches::FCSW2::type> init_FCSW2_map() {
        std::map<std::string, switches::FCSW2::type> ans;
        ans["Act365Fixed"] = FCSW2::Act365Fixed;
        ans["actual/365 (fixed)"] = FCSW2::Act365Fixed;
        ans["Act360"] = FCSW2::Act360;
        ans["actual/360"] = FCSW2::Act360;
        ans["Act365Act"] = FCSW2::Act365Act;
        ans["actual/365 (actual)"] = FCSW2::Act365Act;
        ans["ISDA30360"] = FCSW2::ISDA30360;
        ans["30/360 (ISDA)"] = FCSW2::ISDA30360;
        ans["ISMA30E360"] = FCSW2::ISMA30E360;
        ans["30E/360 (30/360 ISMA)"] = FCSW2::ISMA30E360;
        return ans;
}
switches::FCSW2::type FCSW2_lookup(const char* fincad_switch) {
        static std::map<std::string, switches::FCSW2::type> switch_map = init_FCSW2_map();
        std::map<std::string, switches::FCSW2::type>::iterator it = switch_map.find(fincad_switch);
        if(it != switch_map.end()) {
                return it->second;
        } else {
                throw FCSwitchLookupError("Bad Match: FCSW2");
        }
}

On dirait que vous voulez aller dans l'autre sens (enum to string, plutôt que string to enum), mais cela devrait être trivial à inverser.

-Brin


la source
1
a) Quelqu'un d'autre trouve-t-il cela absolument illisible? Quelques typedefs et l'utilisation de déclarations amélioreraient considérablement la lisibilité. b) les déclarations statiques locales ne sont pas threadsafe. c) utiliser const string & au lieu de char *, d) qu'en est-il d'inclure la valeur qui n'a pas pu être trouvée dans l'exception lancée?
Alastair
0

Voyez si la syntaxe suivante vous convient:

// WeekEnd enumeration
enum WeekEnd
{
    Sunday = 1,
    Saturday = 7
};

// String support for WeekEnd
Begin_Enum_String( WeekEnd )
{
    Enum_String( Sunday );
    Enum_String( Saturday );
}
End_Enum_String;

// Convert from WeekEnd to string
const std::string &str = EnumString<WeekEnd>::From( Saturday );
// str should now be "Saturday"

// Convert from string to WeekEnd
WeekEnd w;
EnumString<WeekEnd>::To( w, "Sunday" );
// w should now be Sunday

Si tel est le cas, vous voudrez peut-être consulter cet article:
http://www.gamedev.net/reference/snippets/features/cppstringizing/


la source
0

ce bon vieux désordre est mon effort basé sur des morceaux et des morceaux de SO. Le for_each devrait être développé pour prendre en charge plus de 20 valeurs d'énumération. Je l'ai testé sur Visual Studio 2019, clang et gcc. c ++ 11

#define _enum_expand(arg) arg
#define _enum_select_for_each(_,_0, _1, _2,_3,_4, _5, _6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,N, ...) N
#define _enum_for_each_0(_call, arg0,arg1,...)
#define _enum_for_each_1(_call, arg0,arg1) _call(arg0,arg1)
#define _enum_for_each_2(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_1(_call,arg0, __VA_ARGS__))
#define _enum_for_each_3(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_2(_call,arg0, __VA_ARGS__))
#define _enum_for_each_4(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_3(_call,arg0, __VA_ARGS__))
#define _enum_for_each_5(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_4(_call,arg0, __VA_ARGS__))
#define _enum_for_each_6(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_5(_call,arg0, __VA_ARGS__))
#define _enum_for_each_7(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_6(_call,arg0, __VA_ARGS__))
#define _enum_for_each_8(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_7(_call,arg0, __VA_ARGS__))
#define _enum_for_each_9(_call, arg0,arg1, ...) _call(arg0,arg1)  _enum_expand(_enum_for_each_8(_call,arg0, __VA_ARGS__))
#define _enum_for_each_10(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_9(_call,arg0, __VA_ARGS__))
#define _enum_for_each_11(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_10(_call,arg0, __VA_ARGS__))
#define _enum_for_each_12(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_11(_call,arg0, __VA_ARGS__))
#define _enum_for_each_13(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_12(_call,arg0, __VA_ARGS__))
#define _enum_for_each_14(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_13(_call,arg0, __VA_ARGS__))
#define _enum_for_each_15(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_14(_call,arg0, __VA_ARGS__))
#define _enum_for_each_16(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_15(_call,arg0, __VA_ARGS__))
#define _enum_for_each_17(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_16(_call,arg0, __VA_ARGS__))
#define _enum_for_each_18(_call, arg0,arg1, ...) _call(arg0,arg1) _enum_expand(_enum_for_each_17(_call,arg0, __VA_ARGS__))
#define _enum_for_each_19(_call, arg0,arg1, ...) _call(arg) _enum_expand(_enum_for_each_18(_call,arg0, __VA_ARGS__))
#define _enum_for_each(arg, ...) \
    _enum_expand(_enum_select_for_each(_, ##__VA_ARGS__, \
    _enum_for_each_19, _enum_for_each_18, _enum_for_each_17, _enum_for_each_16, _enum_for_each_15, \
    _enum_for_each_14, _enum_for_each_13, _enum_for_each_12, _enum_for_each_11, _enum_for_each_10, \
    _enum_for_each_9,  _enum_for_each_8,  _enum_for_each_7,  _enum_for_each_6,  _enum_for_each_5,  \
    _enum_for_each_4,  _enum_for_each_3,  _enum_for_each_2,  _enum_for_each_1,  _enum_for_each_0)(arg, ##__VA_ARGS__))

#define _enum_strip_args_1(arg0) arg0
#define _enum_strip_args_2(arg0, arg1) arg0, arg1
#define _enum_make_args(...) (__VA_ARGS__)

#define _enum_elem_arity1_1(arg) arg,
#define _enum_elem_arity1( ...) _enum_expand(_enum_elem_arity1_1 __VA_ARGS__)
#define _enum_elem_arity2_1(arg0,arg1) arg0 = arg1,
#define _enum_elem_arity2( ...) _enum_expand(_enum_elem_arity2_1 __VA_ARGS__)

#define _enum_elem_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_elem_select_arity_1(...) _enum_expand(_enum_elem_select_arity_2(__VA_ARGS__, _enum_elem_arity2,_enum_elem_arity1,_))
#define _enum_elem_select_arity(enum_type,...) _enum_expand(_enum_elem_select_arity_1 __VA_ARGS__)(__VA_ARGS__)

#define _enum_str_arity1_1(enum_type,arg) { enum_type::arg,#arg },
#define _enum_str_arity1(enum_type,...) _enum_expand(_enum_str_arity1_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_1 __VA_ARGS__)))
#define _enum_str_arity2_1(enum_type,arg,value) { enum_type::arg,#arg },
#define _enum_str_arity2(enum_type, ...) _enum_expand(_enum_str_arity2_1 _enum_make_args( enum_type, _enum_expand(_enum_strip_args_2 __VA_ARGS__)))
#define _enum_str_select_arity_2(_0, _1, NAME,...) NAME
#define _enum_str_select_arity_1(...) _enum_expand(_enum_str_select_arity_2(__VA_ARGS__, _enum_str_arity2,_enum_str_arity1,_))
#define _enum_str_select_arity(enum_type,...) _enum_expand(_enum_str_select_arity_1 __VA_ARGS__)(enum_type,__VA_ARGS__)

#define error_code_enum(enum_type,...)  enum class enum_type {              \
    _enum_expand(_enum_for_each(_enum_elem_select_arity,enum_type, ##__VA_ARGS__))};  \
    namespace _ ## enum_type ## _detail { \
        template <typename> struct _ ## enum_type ## _error_code{ \
            static const std::map<enum_type, const char*> enum_type ## _map; \
        }; \
            template <typename T> \
            const std::map<enum_type, const char*> _ ## enum_type ## _error_code<T>::enum_type ## _map = { \
                _enum_expand(_enum_for_each(_enum_str_select_arity,enum_type,  ##__VA_ARGS__)) \
        }; \
    } \
    inline const char* get_error_code_name(const enum_type& value) { \
        return _ ## enum_type ## _detail::_ ## enum_type ## _error_code<enum_type>::enum_type ## _map.find(value)->second; \
    } 

error_code_enum(myenum,
    (one, 1),
    (two)
);

qui produit le code suivant

enum class myenum { 
    one = 1,
    two,
};
namespace _myenum_detail {
    template <typename>
    struct _myenum_error_code {
        static const std::map<myenum, const char*> myenum_map;
    };
    template <typename T>
    const std::map<myenum, const char*> _myenum_error_code<T>::myenum_map = {
        { myenum::one, "one" }, 
        { myenum::two, "two" },
    };
}
inline const char* get_error_code_name(const myenum& value) { 
    return _myenum_detail::_myenum_error_code<myenum>::myenum_map.find(value)->second; 
}

Dommage que vous ayez à sauter avec le préprocesseur pour faire cela dans l'un des langages de programmation les plus utilisés au monde ...

rmawatson
la source
0

En utilisant des initialiseurs de tableau désignés, votre tableau de chaînes est indépendant de l'ordre des éléments dans l'énumération:

enum Values {
    Val1,
    Val2
};

constexpr string_view v_name[] = {
    [Val1] = "Value 1",
    [Val2] = "Value 2"
}
Daniel
la source