Frage

Hier ist, was ich zu tun versuchen:

typedef enum { ONE, TWO, THREE } Numbers;

Ich versuche, eine Funktion zu schreiben, die ein Schaltergehäuse ähnlich den folgenden tun würden:

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

Statt in jedem Fall zu definieren, ist es eine Möglichkeit, es zu setzen, das ENUM-Variable, wie ich oben tun möchte?

War es hilfreich?

Lösung

Es gibt keine integrierte Lösung. Der einfachste Weg ist mit einer Reihe von char* wo die int-Wert Indizes der Enum in einen String die beschreibenden Namen des ENUM enthält. Wenn Sie eine spärliche enum (eine, die nicht bei 0 startet oder hat Lücken in der Nummerierung), wo einige der int Mappings hoch genug sind, um ein Array-basierte Mapping unpraktisch zu machen, dann können Sie stattdessen eine Hash-Tabelle verwendet werden.

Andere Tipps

Die Technik von macht etwas sowohl eine C-Kennung und einen String? kann hier verwendet werden.

Wie üblich bei solchen Präprozessor Sachen, Schreiben und Verstehen der Präprozessor Teil kann hart sein, und enthält Makros auf andere Makros vorbei und beinhaltet die Verwendung # und ## Operatoren, wobei jedoch es wirklich einfach ist. Ich finde diese Art sehr nützlich für lange Aufzählungen, wo zweimal die gleiche Liste beibehalten kann wirklich lästig sein.

Werkscode - nur einmal eingegeben hat, in der Regel in der Kopfzeile versteckt:

enumFactory.h:

// expansion macro for enum value definition
#define ENUM_VALUE(name,assign) name assign,

// expansion macro for enum to string conversion
#define ENUM_CASE(name,assign) case name: return #name;

// expansion macro for string 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 *GetString(EnumType dummy); \
  EnumType Get##EnumType##Value(const char *string); \

/// define the access function names
#define DEFINE_ENUM(EnumType,ENUM_DEF) \
  const char *GetString(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 */ \
  } \

Hersteller verwendet

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)

Die Technik kann leicht erweitert werden, so dass XX Makros mehr Argumente akzeptiert, und Sie können auch mehrere Makros vorbereitet XX für unterschiedliche Bedürfnisse zu ersetzen, ähnlich wie die drei I in dieser Probe zur Verfügung gestellt haben.

Vergleich zu X-Makros mit # include / #define / #undef

Dies ist zwar ähnlich wie X-Makros andere erwähnt hat, ich denke, diese Lösung eleganter ist, dass es nicht #undefing alles erfordert, die Sie mehr von den komplizierten Sachen verstecken kann in der Fabrik ist die Header-Datei - die Header-Datei ist etwas, das Sie überhaupt nicht berühren, wenn Sie eine neue Enumeration definieren müssen daher neue Enumerationsdefinition viel kürzer und sauberer ist.

// 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 string 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

Es ist auf jeden Fall eine Möglichkeit, dies zu tun - benutzen X () Makros . Diese Makros verwenden, um den C-Vorprozessor enums, Arrays und Codeblocks aus einer Liste von Quelldaten zu konstruieren. Sie müssen nur neue Elemente in die #define mit dem X () Makro hinzuzufügen. Die switch-Anweisung erweitern würde automatisch.

Ihr Beispiel kann wie folgt geschrieben werden:

 // Source data -- Enum, String
 #define X_NUMBERS \
    X(ONE,   "one") \
    X(TWO,   "two") \
    X(THREE, "three")

 ...

 // Use preprocessor to create the Enum
 typedef enum {
  #define X(Enum, String)       Enum,
   X_NUMBERS
  #undef X
 } Numbers;

 ...

 // Use Preprocessor to expand data into switch statement cases
 switch(num)
 {
 #define X(Enum, String) \
     case Enum:  strcpy(num_str, String); break;
 X_NUMBERS
 #undef X

     default: return 0; break;
 }
 return 1;

Es gibt effizientere Wege (das heißt unter Verwendung von X-Makros ein String-Array und Enum-Index erstellen), aber dies ist die einfachste Demo.

Ich weiß, Sie haben ein paar gute und solide Antworten, aber wissen Sie über den # Operator in dem C-Präprozessor?

Damit können Sie dies tun:

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

KISS. Sie werden alle möglichen anderer Schalter / Fall Dinge mit Ihrer Aufzählung tun so, warum Druck anders sein sollte? einen Fall, in Ihrer Druckroutine Vergessens ist keine große Sache, wenn Sie es betrachten sind etwa 100 andere Orte, die Sie einen Fall vergessen. kompilieren nur -Wall, die aus nicht-erschöpfende Fall Streichhölzer warnen. verwenden „default“ nicht, weil das wird der Schalter auf Vollständigkeit und Sie werden nicht erhalten Warnungen machen. Stattdessen lassen Sie den Schalter verlassen und befassen sich mit dem Standard-Fall, wie so ...

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

C oder C ++ unterstützt diese Funktionalität nicht zur Verfügung stellen, obwohl ich es oft gebraucht habe.

Der folgende Code funktioniert, obwohl es für Nicht-Sparse Aufzählungen am besten geeignet ist.

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

Mit dem Nicht-Sparse, ich meine nicht von der Form

typedef enum { ONE, FOUR_THOUSAND = 4000 } Numbers;

da das hat große Lücken.

Der Vorteil dieser Methode ist, dass es die Definitionen der Aufzählungen und Streicher in der Nähe von ihnen setzen; eine switch-Anweisung in einer Funktion spearates sie. Das heißt, Sie sind weniger wahrscheinlich, dass das eine ohne das andere ändern.

Versuchen Sie Konvertieren von C ++ Aufzählungen reiht. Die Kommentare Verbesserungen haben, die das Problem, wenn Enum lösen Elemente haben beliebige Werte.

Die Verwendung von boost :: Präprozessor macht eine elegante Lösung wie folgt möglich:

Schritt 1: schließen Sie die Header-Datei:

#include "EnumUtilities.h"

Schritt 2: deklariert das Aufzählungsobjekt mit folgenden Syntax:

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

Schritt 3: Verwenden Sie Ihre Daten:

Getting die Anzahl der Elemente:

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

Getting die zugehörige Saite:

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;

Erhalten des ENUM-Wert aus dem zugehörigen string:

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;

Das sieht sauber und kompakt, ohne zusätzliche Dateien enthalten. Der Code, den ich innerhalb EnumUtilities.h geschrieben hat, ist die folgende:

#include <boost/preprocessor/seq/for_each.hpp>
#include <string>

#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::string eName##2String(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::string eStrEl) \
    { \
        BOOST_PP_SEQ_FOR_EACH(MACRO1_ENUM, , SEQ) \
        return (enum eName)0; \
    };

Es gibt einige Einschränkungen, das heißt diejenigen von boost :: Präprozessor. In diesem Fall kann die Liste der Konstanten nicht größer sein als 64 Elemente.

Nach der gleichen Logik könnte man auch spärliche Enum erstellen denkt an:

#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::string eName##2String(const enum eName eel) \
    { \
        switch (eel) \
        { \
        BOOST_PP_SEQ_FOR_EACH(MACRO2_STRING, _, SEQ) \
        default: return "Unknown enumerator value."; \
        }; \
    };  

In diesem Fall ist die Syntax:

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

Die Verwendung ist ähnlich wie oben (minus der 2Enum Funktion ## ENAME, dass Sie von der vorherige Syntax extrapolieren könnten versuchen).

Ich habe es getestet auf Mac und Linux, aber beachten Sie, dass boost :: Präprozessor nicht vollständig tragbar sein kann.

Durch einige der Techniken, hier verschmelzen kam ich mit der einfachsten Form bis:

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

Wenn Sie gcc verwenden, ist es möglich, zu verwenden:

const char * enum_to_string_map[]={ [enum1]='string1', [enum2]='string2'};

Dann rufen Sie einfach zum Beispiel

enum_to_string_map[enum1]

, um die Ideen Schauen Sie sich unter Mu Dynamics Research Labs - Blog-Archiv . Ich fand diese früher in diesem Jahr - ich den genauen Kontext vergessen, wo ich über sie kam - und haben es in diesem Code angepasst. Wir können die Verdienste Debatte eine E an der Vorderseite der Zugabe; sie ist auf das spezifische Problem angesprochen, aber nicht Teil einer allgemeinen Lösung. Ich verstaute dies in meinem ‚Vignetten‘ -Ordner entfernt - wo ich interessante Fetzen Code aufbewahren, falls ich möchte, dass sie später. Ich bin peinlich zu sagen, dass ich keine Kenntnis davon, wo diese Idee kam von damals behalten hat.

Rubrik: 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 */

Beispiel Quelle:

/*
@(#)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 <stdio.h>

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

Nicht unbedingt der sauberste Nutzung des C Pre-Prozessor der Welt - aber es verhindern, dass das Material mehrmals auszuschreiben

.

Wenn der ENUM-Index 0-basiert, können Sie die Namen in einem Array von char * setzen, und indizieren sie mit dem ENUM-Wert.

#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

Ich habe eine einfache Templat-Klasse streamable_enum erstellt, die Stream-Betreiber << und >> verwendet und basiert auf dem std::map<Enum, std::string>:

#ifndef STREAMABLE_ENUM_HPP
#define STREAMABLE_ENUM_HPP

#include <iostream>
#include <string>
#include <map>

template <typename E>
class streamable_enum
{
public:
    typedef typename std::map<E, std::string> tostr_map_t;
    typedef typename std::map<std::string, E> fromstr_map_t;

    streamable_enum()
    {}

    streamable_enum(E val) :
        Val_(val)
    {}

    operator E() {
        return Val_;
    }

    bool operator==(const streamable_enum<E>& e) {
        return this->Val_ == e.Val_;
    }

    bool operator==(const E& e) {
        return this->Val_ == e;
    }

    static const tostr_map_t& to_string_map() {
        static tostr_map_t to_str_(get_enum_strings<E>());
        return to_str_;
    }

    static const fromstr_map_t& from_string_map() {
        static fromstr_map_t from_str_(reverse_map(to_string_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 <typename E>
streamable_enum<E> stream_enum(E e) {
    return streamable_enum<E>(e);
}

template <typename E>
typename streamable_enum<E>::tostr_map_t get_enum_strings() {
    // \todo throw an appropriate exception or display compile error/warning
    return {};
}

template <typename E>
std::ostream& operator<<(std::ostream& os, streamable_enum<E> e) {
    auto& mp = streamable_enum<E>::to_string_map();
    auto res = mp.find(e);
    if (res != mp.end()) {
        os << res->second;
    } else {
        os.setstate(std::ios_base::failbit);
    }
    return os;
}

template <typename E>
std::istream& operator>>(std::istream& is, streamable_enum<E>& e) {
    std::string str;
    is >> str;
    if (str.empty()) {
        is.setstate(std::ios_base::failbit);
    }
    auto& mp = streamable_enum<E>::from_string_map();
    auto res = mp.find(str);
    if (res != mp.end()) {
        e = res->second;
    } else {
        is.setstate(std::ios_base::failbit);
    }
    return is;
}

#endif

Verbrauch:

#include "streamable_enum.hpp"

using std::cout;
using std::cin;
using std::endl;

enum Animal {
    CAT,
    DOG,
    TIGER,
    RABBIT
};

template <>
streamable_enum<Animal>::tostr_map_t get_enum_strings<Animal>() {
    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<Animal>::to_string_map()) {          // Use from_string_map() and pr.first instead
        cout << " " << pr.second << endl;                               // to have them sorted in alphabetical order
    }
    streamable_enum<Animal> 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;
}

Hier wird eine Lösung mit Hilfe von Makros mit den folgenden Merkmalen:

  1. nur jeden Wert der Enum-write once, so gibt es keine Doppel Listen

  2. halten
  3. nicht den Enum-Wert in einer separaten Datei halten, die später #include ist, so kann ich es schreiben, wo immer ich will

  4. ersetzt nicht die ENUM selbst, ich will noch den Aufzählungstyp definiert haben, aber neben er Ich möchte jeden Enum-Namen des entsprechenden Zeichenfolge zuordnen können (nicht Legacy-Code beeinflussen)

  5. sollte die Suche schnell, so vorzugsweise keinen Schalter-Fall für die großen Aufzählungen

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

dachte ich, dass eine Lösung wie Boost.Fusion eine für die Anpassung structs und Klassen wäre schön, sie es auch irgendwann hatte, Aufzählungen als Fusionssequenz verwendet werden.

Also habe ich nur ein paar kleine Makros, den Code zu erzeugen, um die Aufzählungen zu drucken. Dies ist nicht perfekt und hat nichts zu sehen, mit Boost.Fusion vorformulierten Code generiert, kann aber wie die Boost-Fusion Makros verwendet werden. Ich möchte generieren wirklich die von Boost.Fusion benötigten Typen in dieser Infrastruktur zu integrieren, die Namen von Strukturkomponenten drucken können, aber dies später geschehen wird, denn jetzt ist dies nur Makros:

#ifndef SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP
#define SWISSARMYKNIFE_ENUMS_ADAPT_ENUM_HPP

#include <swissarmyknife/detail/config.hpp>

#include <string>
#include <ostream>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>


#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::string to_string(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

Die alte Antwort unten ziemlich schlecht ist, bitte nicht, dass verwenden. :)

Alte Antwort:

Ich habe einen Weg gesucht, die dieses Problem löst, ohne zu viel die Aufzählungen Erklärung Syntax zu ändern. Ich kam zu einer Lösung, die den Präprozessor verwendet eine Zeichenfolge aus einer Zeichenfolge Enum-Deklaration abgerufen werden.

Ich bin in der Lage nicht spärlich Aufzählungen wie folgt zu definieren:

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

Und ich kann mit ihnen auf verschiedene Weise interagieren:

// With a stringstream
std::stringstream 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);

Basierend auf den folgenden Definitionen:

#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<enumTypeArg>(#__VA_ARGS__, str);   \
    }                                                                                    \
}                                                                                        \


namespace swissarmyknife { namespace enums {

    static inline std::string to_string(const std::string completeEnumDeclaration, size_t enumVal) throw (std::runtime_error) {
        size_t begin = completeEnumDeclaration.find_first_of('{');
        size_t end = completeEnumDeclaration.find_last_of('}');
        const std::string 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::string identifiersSubset = identifiers.substr(0, found);
                size_t beginId = identifiersSubset.find_last_of("{,");
                identifiersSubset = identifiersSubset.substr(beginId+1);
                boost::algorithm::trim(identifiersSubset);
                return identifiersSubset;
            }

            ++count;
        } while (found != std::string::npos);

        throw std::runtime_error("The enum declaration provided doesn't contains this state.");
    }                                                  

    template <typename EnumType>
    static inline EnumType from_string(const std::string completeEnumDeclaration, const std::string &enumStr) throw (std::runtime_error) {
        size_t begin = completeEnumDeclaration.find_first_of('{');
        size_t end = completeEnumDeclaration.find_last_of('}');
        const std::string identifiers = completeEnumDeclaration.substr(begin + 1, end );

        size_t count = 0;
        size_t found = 0;
        do {
            found = identifiers.find_first_of(",}", found+1);

            std::string identifiersSubset = identifiers.substr(0, found);
            size_t beginId = identifiersSubset.find_last_of("{,");
            identifiersSubset = identifiersSubset.substr(beginId+1);
            boost::algorithm::trim(identifiersSubset);

            if (identifiersSubset == enumStr) {
                return static_cast<EnumType>(count);
            }

            ++count;
        } while (found != std::string::npos);

        throw std::runtime_error("No valid enum value for the provided string");
    }                      

}}

Wenn ich Unterstützung brauche für spärliche Enum und wenn ich mehr Zeit habe, werde ich die to_string und from_string Implementierungen mit boost :: Xpressive zu verbessern, aber dies wird die Kosten in der Kompilierung wegen der wichtigen Templat durchgeführt, und die ausführbare Datei erzeugt wird wahrscheinlich wirklich größer sein. Das hat aber den Vorteil, dass es besser lesbar und maintanable als dieser hässliche manuelle String-Manipulation Code sein wird. : D

Ansonsten immer Boost-I verwendet :: bimap solche Zuordnungen zwischen Aufzählungen Wert und Zeichenfolge auszuführen, aber es muss manuell gepflegt werden.

Weil ich nicht lieber Makros für alle üblichen Gründen verwenden, habe ich eine begrenztere Makro-Lösung, die den Vorteil der Beibehaltung der Enum-Deklaration Makro frei hat. Nachteile sind die Makro Defintion Paste für jede Enum kopieren zu müssen, und mit explizit ein Makro-Aufruf hinzufügen, wenn Werte der Enum hinzufügen.

std::ostream& operator<<(std::ostream& os, provenance_wrapper::CaptureState cs)
{
#define HANDLE(x) case x: os << #x; break;
    switch (cs) {
    HANDLE(CaptureState::UNUSED)
    HANDLE(CaptureState::ACTIVE)
    HANDLE(CaptureState::CLOSED)
    }
    return os;
#undef HANDLE
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top