Frage

Angenommen, wir haben einige genannt Aufzählungen:

enum MyEnum {
      FOO,
      BAR = 0x50
};

Was ich googeln für ein Skript (alle Sprachen), die alle Header in meinem Projekt durchsucht und erzeugt einen Header mit einer Funktion pro Enum.

char* enum_to_string(MyEnum t);

Und eine Implementierung mit so etwas wie folgt aus:

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

Die Gotcha ist wirklich mit typedefed Aufzählungen und unbenannte C Stil Aufzählungen. Weiß jemand etwas dafür?

EDIT: Die Lösung sollte meine Quelle nicht ändern, mit Ausnahme der generierten Funktionen. Die Aufzählungen sind in einer API zu verwenden, da die Lösungen bisher vorgeschlagen ist nicht nur eine Option.

War es hilfreich?

Lösung

Sie möchten überprüfen, GCCXML .

Ausführen GCCXML auf Ihrem Beispielcode erzeugt:

<GCC_XML>
  <Namespace id="_1" name="::" members="_3 " mangled="_Z2::"/>
  <Namespace id="_2" name="std" context="_1" members="" mangled="_Z3std"/>
  <Enumeration id="_3" name="MyEnum" context="_1" location="f0:1" file="f0" line="1">
    <EnumValue name="FOO" init="0"/>
    <EnumValue name="BAR" init="80"/>
  </Enumeration>
  <File id="f0" name="my_enum.h"/>
</GCC_XML>

Sie könnten jede beliebige Sprache verwenden Sie lieber die Enumeration und EnumValue Tags ziehen und den gewünschten Code zu generieren.

Andere Tipps

X-Makros sind die beste Lösung. Beispiel:

#include <iostream>

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)

Allerdings habe ich in der Regel die folgende Methode bevorzugen, so dass es möglich ist die Zeichenfolge ein wenig zwicken.

#define X(a, b) a,
#define X(a, b) b,

X(Red, "red")
X(Green, "green")
// etc.

@hydroo: Ohne die zusätzliche Datei:

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

Was ich neige dazu, zu tun, ist ein C-Array mit den Namen in der gleichen Reihenfolge und Position wie die ENUM-Werte erstellen.

zB.

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

dann können Sie das Array an Orten, wo man einen Menschen lesbaren Wert wollen, zB

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

Sie können ein wenig mit dem Zeichenfolge Operator experimentieren (# in der Vorverarbeitungsbefehl Referenz sehen), die das tun, was Sie wollen, in einigen circumstances- zB:

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

wird „rot“ auf die Standardausgabe drucken. Leider wird es nicht für eine Variable arbeiten (wie Sie die Variablennamen ausgedruckt werden erhalten)

Ich habe ein unglaublich einfaches Makro zu verwenden, die diese in einer vollständig trocken Art und Weise tut. Es geht um variadische Makros und einige einfache Parsing-Magie. Hier geht:

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

Um dies in Ihrem Code zu verwenden, einfach tun:

AWESOME_MAKE_ENUM(Animal,
    DOG,
    CAT,
    HORSE
);

QT ist in der Lage, dass die (dank der Metaobjekt-Compiler) zu ziehen: link

Ich habe gerade dieses Rad heute neu erfunden, und ich dachte, es würde Aktie.

Diese Implementierung funktioniert nicht erfordert keine Änderungen an den Code, der die Konstanten definiert, die Aufzählungen oder #defines oder irgendetwas anderes, das auf eine ganze Zahl zufällt sein kann - in meinem Fall hatte ich definieren Symbole in Bezug auf andere Symbole. Es funktioniert auch gut mit spärlichen Werten. Es ermöglicht auch mehrere Namen für den gleichen Wert, immer die erste Rückkehr. Der einzige Nachteil ist, dass es erfordert, dass Sie eine Tabelle der Konstanten zu machen, die out-of-date werden könnte als neue beispielsweise hinzugefügt werden.

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<int N>
const char * IdToName(int id, IdAndName (&table)[N])
{
   return IdToName(id, &table[0], &table[N]);
}

Ein Beispiel, wie Sie es verwenden würden:

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

Die IdToName Funktion beruht auf std::lower_bound schnelle Lookups zu tun, die die Tabelle sortiert werden muss. Wenn die ersten beiden Einträge in der Tabelle aus der Ordnung sind, wird die Funktion automatisch sortieren.

Edit: Ein Kommentar hat mich an eine andere Art und Weise denken das gleiche Prinzip der Verwendung. Ein Makro vereinfacht die Erzeugung einer großen switch Aussage.

#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 stringify( name ) # name

enum MyEnum {
    ENUMVAL1
};
...stuff...

stringify(EnumName::ENUMVAL1);  // Returns MyEnum::ENUMVAL1

Eine weitere Diskussion über diese Methode

Präprozessordirektive Tricks für Einsteiger

Interessante die Anzahl der Möglichkeiten zu sehen. hier ist ein i vor langer Zeit verwendet:

in der Datei myenummap.h:

#include <map>
#include <string>
enum test{ one, two, three, five=5, six, seven };
struct mymap : std::map<unsigned int, std::string>
{
  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(){};
};

in main.cpp

#include "myenummap.h"

...
mymap nummap;
std::cout<< nummap[ one ] << std::endl;

Es ist nicht const, aber sein bequem.

Hier ist eine andere Art und Weise, die C ++ 11-Funktionen verwendet. Dies ist const, keine STL-Container erben und ist ein wenig aufgeräumter:

#include <vector>
#include <string>
#include <algorithm>
#include <iostream>

//These stay together and must be modified together
enum test{ one, two, three, five=5, six, seven };
std::string enum_to_str(test const& e)
{
    typedef std::pair<int,std::string> mapping;
    auto m = [](test const& e,std::string const& s){return mapping(static_cast<int>(e),s);}; 
    std::vector<mapping> 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<int>(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) ) << "]"<<std::endl; //returns an empty string
    std::cout<< "Enumval five to string : ["<< enum_to_str( five ) << "] "<< std::endl; //works
    return 0;
}

Dies kann in C erfolgen ++ 11

#include <map>
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;
}
#include <stdarg.h>
#include <algorithm>
#include <string> 
#include <vector>
#include <sstream>
#include <map>

#define SMART_ENUM(EnumName, ...)                                   \
class EnumName                                                      \
{                                                                   \
private:                                                            \
    static std::map<int, std::string> nameMap;                      \
public:                                                             \
    enum {__VA_ARGS__};                                             \
private:                                                            \
    static std::map<int, std::string> initMap()                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        map<int, string> tmp;                                       \
        for(vector<string>::iterator it = strings.begin();          \
                                               it != strings.end(); \
                                               ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream 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::string toString(int aInt)                           \
    {                                                               \
        return nameMap[aInt];                                       \
    }                                                               \
};                                                                  \
std::map<int, std::string>                                          \
EnumName::nameMap = EnumName::initMap();

Verbrauch:

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

Ein andere Antwort: in manchen Kontexten, macht es Sinn, Ihre Aufzählung in einem Nicht-Code-Format zu definieren, wie eine CSV, YAML oder XML-Datei, und erzeugt sowohl den C ++ Aufzählungscode und den zu-String-Code aus dem Definition. Dieser Ansatz kann oder in Ihrer Anwendung nicht sinnvoll sein, aber es ist etwas im Auge zu behalten.

Dies ist eine Modifikation @ user3360260 Antwort. Es hat die folgenden neuen Funktionen

  • MyEnum fromString(const string&) Unterstützung
  • kompiliert mit Visual Studio 2012
  • die ENUM ist ein tatsächlicher POD-Typ (nicht nur const Deklarationen), so können Sie es zu einem Variablen zuweisen.
  • hinzugefügt C ++ "Bereich" -Funktion (in Form von Vektor) "foreach" Iteration über Enum zu ermöglichen

Verbrauch:

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

Hier ist der 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<int, std::string>& tmp)                     \
    {                                                               \
        using namespace std;                                        \
                                                                    \
        int val = 0;                                                \
        string buf_1, buf_2, str = #__VA_ARGS__;                    \
        replace(str.begin(), str.end(), '=', ' ');                  \
        stringstream stream(str);                                   \
        vector<string> strings;                                     \
        while (getline(stream, buf_1, ','))                         \
            strings.push_back(buf_1);                               \
        for(vector<string>::iterator it = strings.begin();          \
                                                it != strings.end(); \
                                                ++it)                \
        {                                                           \
            buf_1.clear(); buf_2.clear();                           \
            stringstream 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::string toString(void) const {                              \
            return toString(value);                                 \
    }                                                               \
    static std::string toString(int aInt)                           \
    {                                                               \
        return nameMap()[aInt];                                     \
    }                                                               \
    static EnumName fromString(const std::string& s)                \
    {                                                               \
        auto it = find_if(nameMap().begin(), nameMap().end(), [s](const std::pair<int,std::string>& 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<int,std::string>& nameMap() {                   \
      static std::map<int,std::string> nameMap0;                    \
      if (nameMap0.size() ==0) initMap(nameMap0);                   \
      return nameMap0;                                              \
    }                                                               \
    static std::vector<EnumName> allValues() {                      \
      std::vector<EnumName> x{ __VA_ARGS__ };                       \
      return x;                                                     \
    }                                                               \
    bool operator<(const EnumName a) const { return (int)*this < (int)a; } \
};         

Beachten Sie, dass die Umwandlung toString ist eine schnelle hat Lookup, während die Umwandlung vonString eine langsame lineare Suche ist. Aber Strings sind so teuer sowieso (und die zugehörige Datei IO), ich habe nicht das Gefühl, die Notwendigkeit, zu optimieren oder eine bimap zu verwenden.

Suma Makrolösung ist schön. Sie brauchen nicht zwei verschiedene Makros zu haben, obwohl. C ++ wil gerne einen Header zweimal enthalten. lassen Sie einfach die Wache sind.

So haben Sie einen foobar.h haben würde definieren, nur

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

, und Sie würden es so gehören:

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

enumfactory.h wird 2 #include ENUMFACTORY_ARGUMENTs tun. In der ersten Runde, dehnt es sich ENUM wie Suma des DECLARE_ENUM; in der zweiten Runde funktioniert ENUM wie DEFINE_ENUM.

Sie können enumfactory.h mehrere Male umfassen auch so lange wie Sie in verschiedenen # passieren definieren die für ENUMFACTORY_ARGUMENT

Beachten Sie, dass Ihre Conversion-Funktion sollte idealerweise eine const zurückkehren char *.

Wenn Sie sich leisten können Ihre Aufzählungen in ihren separaten Header-Dateien zu setzen, Sie vielleicht so etwas wie dies mit Makros tun könnten (oh, wird dies hässlich sein):

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

Wo enum_def.h hat:

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

Und enum_conv.h hat:

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

Und schließlich colour.h hat:

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

Und Sie können die Konvertierungsfunktion als verwenden:

printf("%s", colour_to_string(colour::red));
nur an einem einzigen Ort im Code

Das ist hässlich, aber es ist der einzige Weg (am Präprozessor-Ebene), die Sie Ihre Enum können definieren. Ihr Code ist daher nicht anfällig für Fehler aufgrund von Änderungen an der ENUM. Ihre Enum-Definition und die Konvertierungsfunktion wird immer synchron sein. Aber ich wiederhole, das ist hässlich:)

Ich tue dies mit separaten Side-by-Side-Enum-Wrapper-Klassen, die mit Makros erzeugt werden. Es gibt mehrere Vorteile:

  • Kann generieren sie für Aufzählungen ich nicht definieren (zB: OS-Plattform-Header Aufzählungen)
  • Kann integrieren Bereich in die Wrapper-Klasse Überprüfung
  • Kann tun „intelligente“ Formatierung mit Bitfeld Aufzählungen

Der Nachteil ist natürlich, dass ich die Enum-Werte in den Formatter Klassen duplizieren, und ich habe kein Skript, sie zu erzeugen. Other than that, obwohl, so scheint es ziemlich gut zu funktionieren.

Hier ist ein Beispiel für eine Enum von meiner Code-Basis, sans all Framework-Code, der die Makros und Templates implementiert, aber man kann auf die Idee kommen:

enum EHelpLocation
{
    HELP_LOCATION_UNKNOWN   = 0, 
    HELP_LOCAL_FILE         = 1, 
    HELP_HTML_ONLINE        = 2, 
};
class CEnumFormatter_EHelpLocation : public CEnumDefaultFormatter< EHelpLocation >
{
public:
    static inline CString 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;

Die Idee ist dann anstelle EHelpLocation zu verwenden, verwenden Sie SEHelpLocation; alles funktioniert auf die gleiche, aber Sie bekommen Bereichsprüfung und eine ‚Format ()‘ Methode auf dem ENUM-Variable selbst. Wenn Sie einen eigenständigen Wert zu formatieren müssen, können Sie CEnumFormatter_EHelpLocation :: FormatEnum (...).

Hoffe, dass dies hilfreich ist. Ich weiß, das ist auch nicht die ursprüngliche Frage zu einem Skript-Adresse, um tatsächlich die andere Klasse zu generieren, aber ich hoffe, dass die Struktur hilft jemand versucht, das gleiche Problem zu lösen, oder schreiben Sie ein solches Skript.

Hier ist eine one-Datei Lösung (basierend auf elegante Antwort von @Marcin:

#include <iostream>

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

Dies war meine Lösung mit BOOST:

#include <boost/preprocessor.hpp>

#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 QString 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 <typename T>                                                       \
    inline const T strToEnum(QString 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());                        \
    }

Enum erstellen, deklarieren:

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

Für Umrechnungen:

SERVICE_RELOAD serviceReloadEnum = strToEnum<SERVICE_RELOAD>("reload_log");
QString serviceReloadStr = enumToStr(reload_log);

Hinzufügen von noch mehr Einfachheit in der Anwendung zu Jasper Bekkers' fantastischen Antwort :

Richten Sie einmal:

#define MAKE_ENUM(VAR) VAR,
#define MAKE_STRINGS(VAR) #VAR,
#define MAKE_ENUM_AND_STRINGS(source, enumName, enumStringName) \
    enum enumName { \
    source(MAKE_ENUM) \
    };\
const char* const enumStringName[] = { \
    source(MAKE_STRINGS) \
    };

Dann wird für Nutzung:

#define SOME_ENUM(DO) \
    DO(Foo) \
    DO(Bar) \
    DO(Baz)
...
MAKE_ENUM_AND_STRINGS(SOME_ENUM, someEnum, someEnumNames)

Ein Problem mit Antwort 0 ist, dass die Enum binäre Werte müssen nicht bei 0 beginnen und sind nicht notwendigerweise zusammenhängend ist.

Wenn ich brauche das, ich in der Regel:

  • Ziehen Sie die Enumerationsdefinition in meine Quelle
  • bearbeiten, um es nur die Namen
  • ein Makro tun, den Namen auf den Fall Klausel in der Frage zu ändern, doch in der Regel in einer Zeile: case foo: return „foo“;
  • fügen Sie den Schalter, Ausfall- und andere Syntax es legal machen

Der folgende Ruby-Skript versucht, die Header zu analysieren und builts die erforderlichen Quellen neben den ursprünglichen Header.

#! /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_string_h_
#define <%= enum_name %>_to_string_h_ 1

#include "<%= header %>"
char* enum_to_string(<%= enum_name %> e);

#endif
EOS

template_cpp = ERB.new <<-EOS
#include "<%= enum_name %>_to_string.h"

char* enum_to_string(<%= 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_string.h", 'wb') { |built_h|
      built_h.write(template_h.result(binding))
    }

    File.open("#{File.dirname(header)}/#{enum_name}_to_string.cpp", 'wb') { |built_cpp|
      built_cpp.write(template_cpp.result(binding))
    }
  }
}

Verwenden von regulären Ausdrücken macht diesen „Parser“ ziemlich zerbrechlich, es kann Ihren speziellen Header anmutig nicht in der Lage zu handhaben.

Angenommen, Sie haben einen Header toto / A. h haben, enthält Definitionen für Aufzählungen MyEnum und MyEnum2. Das Skript erstellen:

toto/MyEnum_to_string.h
toto/MyEnum_to_string.cpp
toto/MyEnum2_to_string.h
toto/MyEnum2_to_string.cpp

Weitere robuste Lösungen seien:

  • Erstellen Sie alle Quellen Aufzählungen und ihre Operationen aus einer anderen Quelle zu definieren. Dies bedeutet, dass Sie Ihre Aufzählungen in einem XML / YML / was auch immer Datei definieren, die viel einfacher ist, zu analysieren als C / C ++.
  • Verwenden Sie einen echten Compiler wie vorgeschlagen durch Avdi.
  • Verwenden Sie Präprozessormakros mit oder ohne Vorlagen.

Es ist nicht freigegebene Software, aber es scheint BOOST_ENUM von Frank Laub könnte die Rechnung passen. Der Teil wo ich darüber gefällt, ist, dass Sie eine ENUM im Rahmen einer Klasse definieren, die die meisten der Makro-basierten Aufzählungen in der Regel lassen Sie nicht tun. Es ist in der Boost-Vault befindet sich unter: http : //www.boostpro.com/vault/index.php action = Downloadfile & filename = enum_rev4.6.zip & directory = & Es hat keine Entwicklung seit 2006 gesehen, also weiß ich nicht, wie gut es mit den neuen Boost-Versionen kompiliert. Schauen Sie unter libs / Test für ein Beispiel der Nutzung.

Ich möchte dies schreiben, falls jemand es nützlich findet.

In meinem Fall, ich brauche einfach ToString() und FromString() Funktionen für ein einzelnes C ++ 11 Enum aus einer einzigen .hpp-Datei zu erzeugen.

Ich schrieb einen Python-Skript, das die Header-Datei enthält, die Enum Elemente und erzeugt die Funktionen in einer neuen .cpp Datei analysiert.

Sie können dieses Skript in CMakeLists.txt hinzufügen mit execute_process oder als Pre-Build-Ereignis in Visual Studio. Die .cpp Datei automatisch generiert werden, ohne die Notwendigkeit, manuell ein neues Enum Element jedes Mal zu aktualisieren, wird hinzugefügt.

generate_enum_strings.py

# This script is used to generate strings 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.strip, 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::string ToString(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 + ' FromString(const std::string &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)

Beispiel:

ErrorCode.hpp

#pragma once

#include <string>
#include <cstdint>

namespace myns
{
    enum class ErrorCode : uint32_t
    {
        OK = 0,
        OutOfSpace,
        ConnectionFailure,
        InvalidJson,
        DatabaseFailure,
        HttpError,
        FileSystemError,
        FailedToEncrypt,
        FailedToDecrypt,
        EndOfFile,
        FailedToOpenFileForRead,
        FailedToOpenFileForWrite,
        FailedToLaunchProcess,

        Last
    };

    std::string ToString(ErrorCode errorCode);
    ErrorCode FromString(const std::string &errorCode);
}

Ausführen python generate_enum_strings.py ErrorCode.hpp

Ergebnis:

ErrorCode.cpp

#include "ErrorCode.hpp"

namespace myns
{
    std::string ToString(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 FromString(const std::string &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;
    }
}

Sie können eine Reflektion Bibliothek verwenden, wie Ponder . Sie können die Aufzählungen registrieren und dann können Sie sie hin und her mit der API konvertieren.

enum class MyEnum
{
    Zero = 0,
    One  = 1,
    Two  = 2
};

ponder::Enum::declare<MyEnum>()
    .value("Zero", MyEnum::Zero)
    .value("One",  MyEnum::One)
    .value("Two",  MyEnum::Two);

ponder::EnumObject zero(MyEnum::Zero);

zero.name(); // -> "Zero"

Das ist so ziemlich der einzige Weg, es kann getan werden (ein Array von String könnte funktioniert auch).

Das Problem ist, wenn ein C-Programm kompiliert wird, alle der binäre Wert der Enumeration ist, die verwendet werden, und der Name ist weg.

Hier ist ein CLI-Programm, das ich schrieb, einfach Aufzählungen in Strings zu konvertieren. Es ist einfach zu bedienen, und dauert ca. 5 Sekunden, um es (einschließlich der Zeit auf CD in das Verzeichnis mit dem Programm, dann ausführen, um die Datei Passieren des Enum enthält) getan.

Hier herunterladen: http://www.mediafire.com/?nttignoozzz

Diskussionsthema auf es hier: http: //cboard.cprogramming. com / projects-job-Rekrutierung / 127488-Frei Programm-im-Sharing-convertenumtostrings.html

Starten Sie das Programm mit dem „--help“ Argumente eine Beschreibung zu bekommen, wie es zu benutzen.

nicht allzu langer Zeit habe ich einige Trick Aufzählungen haben richtig in QComboBox angezeigt und als eine Anweisung Definition von ENUM und String-Darstellungen zu haben

#pragma once
#include <boost/unordered_map.hpp>

namespace enumeration
{

   struct enumerator_base : boost::noncopyable
   {
      typedef
         boost::unordered_map<int, std::wstring>
         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<class T>
   struct enumerator;

   template<class D>
   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<NAME>\
   : enum_singleton< enumerator<NAME> >\
{                      \
   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)))))
*/

Jetzt hast du enumeration::enum_singleton<your_enum>::instance() können Aufzählungen in Strings konvertieren. Wenn Sie kv_storage_t mit boost::bimap ersetzen, werden Sie auch rückwärts Umwandlung der Lage zu tun. Gemeinsame Basisklasse für Konverter eingeführt wurde es in Qt Objekt zu speichern, weil Qt Objekte nicht Vorlagen sein könnten

Vorherige Auftritt

Als Variante mit einfachen lib> http : //codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

Im Code

#include <EnumString.h>

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

Zeilen hinzufügen

Begin_Enum_String( FORM )
{
    Enum_String( F_NONE );
    Enum_String( F_BOX );
    Enum_String( F_CUBE );
    Enum_String( F_SPHERE );
}
End_Enum_String;

funktionieren, , wenn Werte in Enum nicht dublicate .

Beispiel für die Verwendung

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

und vice versa

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

Hier ist ein Versuch, << und >> Stream Operatoren auf Enum automatisch mit einer Linie Makrobefehl zu bekommen nur ...

Definitionen:

#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]); \
    }

Verbrauch:

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

über die Grenzen dieser Regelung allerdings nicht sicher, ... Kommentare sind willkommen!

#include <iostream>
#include <map>
#define IDMAP(x) (x,#x)

std::map<int , std::string> enToStr;
class mapEnumtoString
{
public:
    mapEnumtoString(){  }
    mapEnumtoString& operator()(int i,std::string str)
    {
        enToStr[i] = str;
        return *this;
    }
public:
   std::string operator [] (int i)
    {
        return enToStr[i];
    }

};
mapEnumtoString k;
mapEnumtoString& init()
{
    return k;
}

int main()
{

init()
    IDMAP(1)
    IDMAP(2)
    IDMAP(3)
    IDMAP(4)
    IDMAP(5);
std::cout<<enToStr[1];
std::cout<<enToStr[2];
std::cout<<enToStr[3];
std::cout<<enToStr[4];
std::cout<<enToStr[5];
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top