enum zu string



  • ... schrieb:

    Ich empfinde diese Variante als wesentlich sinniger:

    enum T
        {
            A = 0,
            B,
            C
        };
    
        char names[] =
        {
            'A',
            'B',
            'C'
        };
    

    Es gibt Situationen, in denen enums nicht lückenlos nummeriert sind und in denen funktionieren solche Varianten nicht.
    Wir haben tw. "innere Strukturierungen" in größeren enum-Definitionen (ist nicht schön, aber bisweilen das kleinste Übel). Da ziehen wir dann doch die "switch-Variante" vor - da ist die Verknüpfung einfach direkter.

    Gruß,

    Simon2.



  • Die switch-Variante geht aber halt auch nicht immer...

    enum x
    {
      A,
      B,
      C = A
    };
    

    Ich weiß zwar nicht mal mehr, wann das bei mir das letzte mal vorkam, aber naja... ^^

    Aber stimmt schon, da ist die Troll.Soft-Variante immernoch die tollste...

    bb



  • unskilled schrieb:

    ...

    enum x
    {
      A,
      B,
      C = A
    };
    

    ...

    😮 Geht das?
    Hätte gedacht, dass da ein Compiler Ärger macht... (und der Standard auch).

    Gruß,

    Simon2.



  • Hat was von deprecated Enum-Einträgen.

    Warum keine std::map<EnumTyp, std::string>?



  • Fellhuhn schrieb:

    Hat was von deprecated Enum-Einträgen.

    Warum keine std::map<EnumTyp, std::string>?

    Ist vom Aufwand her auch nicht besser als switch-case.



  • Hm, okay. Sowas? Gerade beim Googlen drüber gestolpert:

    #define MY_LIST \ 
         X(foo),    \ 
         X(bar),    \ 
         X(baz)
    
    #define X(x) x
    enum eMyEnum
    {
    	MY_LIST
    };
    #undef X
    #define X(x) #x
    const char *szMyEnum[] =
    {
    	MY_LIST
    };
    #undef X
    


  • unskilled schrieb:

    Die switch-Variante geht aber halt auch nicht immer...

    enum x
    {
      A,
      B,
      C = A
    };
    

    Ich weiß zwar nicht mal mehr, wann das bei mir das letzte mal vorkam, aber naja...

    Ich weiß noch genau, wann dieser Fall "C = A" bei mir vorkam. Und das ich ihn nicht gelöst habe. Wenn jemand eine Idee hat, heraus damit.

    tschüß
    Troll.Soft



  • In solch einem exotischen Fall kann man A und C nicht unterscheiden, denn sie haben einen identischen Wert. Wozu sollte man sie auch unterscheiden sollen? A ist C, und C ist A.



  • Janjan schrieb:

    In solch einem exotischen Fall kann man A und C nicht unterscheiden, denn sie haben einen identischen Wert. Wozu sollte man sie auch unterscheiden sollen? A ist C, und C ist A.

    Identischer Wert muss nichts heißen. Bei int a = 0, b = a; kann ich a und b über &a und &b auch unterscheiden. Bei enum-Werten geht das aber nicht, da sie nur symbolische Kostanten sind und keine Adresse besitzen.



  • ipsec schrieb:

    Janjan schrieb:

    In solch einem exotischen Fall kann man A und C nicht unterscheiden, denn sie haben einen identischen Wert. Wozu sollte man sie auch unterscheiden sollen? A ist C, und C ist A.

    Identischer Wert muss nichts heißen. Bei int a = 0, b = a; kann ich a und b über &a und &b auch unterscheiden. Bei enum-Werten geht das aber nicht, da sie nur symbolische Kostanten sind und keine Adresse besitzen.

    In dem Fall sind a und b aber Unterschiedliche Dinge. Bei A und C vom Enum nicht.



  • Hm, mit variadic macros könnte man was machen, wenn ich nur wüsste wie man bei __VA_ARGS__ aus jedem Element ein String macht wie es bei #element geht... hmm...



  • Vielleicht sollten wir uns darüber unterhalten, welche genauen Anforderungen an die Enumeration gestellt werden. Die fett geschriebenen wurden bisher genannt, ich habe ein paar weitere mögliche aufgelistet:

    • Keine Codeduplizierung bei der Definition
    • Konvertierbarkeit zu Strings
    • Implizite/Explizite Konvertierbarkeit (von und) zu Ganzzahlen
    • Compilezeitfehler bei Zugriff auf nicht existenten Enumerator
    • Iteration durch alle Enumeratoren
    • ...

    Je mehr Kriterien erfüllt werden müssen, desto mühsamer wird natürlich eine Implementierung. Praktisch wäre auch ein vollständiges (und dennoch möglichst kleines) Beispiel, welches zeigt, was das enum alles leisten soll.



  • Da ist es wohl am einfachsten die gewünschen Enums in einer extra Datei zu verwalten und sich von einem Skript zusammenschustern zu lassen.



  • Toll wäre es, wenn man Enums ähnlich wie in Ada hätte.

    Die haben folgende Eigenschaften:
    Definition

    type My_Enum is (Red, Green, Blue); --Typdefinition
    --fuers Hardwarenahe ist das hier toll:
    for My_Enum'Size use 8; --My_Enum ist 8 Bit gross...
    for My_Enum'Alignment use 1; --...und wird an 1 Byte Grenzen aligned.
    for My_Enum use(Red => 1, Green => 2, Blue => 4); --Speicherrepraesentation festlegen
    

    Konvertierbarkeit von Enum zu String und zurück:

    Enum : My_Enum := Green;
    Str : String := My_Enum'Image(Enum); --Str enthaelt String "Green"
    ...
    Enum := My_Enum'Value("Red");        --Aus String: Enum jetzt Red
    

    Unerscheidung zwischen interner Repräsentation (numerischer Wert im Speicher) und externer Repräsentation (siehe oben).

    Iterierbarkeit über Enumerations:

    for E in My_Enum'Range loop --iteriert über alle Elemente im Enum
       ...
    end loop;
    

    Arrays von Enums:

    type My_Enum_Array is array(My_Enum) of Integer; --Integer Array mit Indizes Red, Green, Blue
    Array : My_Enum_Array;
    ...
    Array(Green) := 10; --geht auch ganz toll mit der Schleife ueber den Range...
    

    Es geht noch mehr, aber das sind die praktischten Eigenschaften.



  • Bleib mir weg mit Ada... so toll manche der Features auch sein mögen, alleine bei den kleinen Schnipseln die du gespostet hast, bekomm ich wieder Kopfschmerzen und Schweißausbrüche. 😉



  • Fellhuhn schrieb:

    Bleib mir weg mit Ada... so toll manche der Features auch sein mögen, alleine bei den kleinen Schnipseln die du gespostet hast, bekomm ich wieder Kopfschmerzen und Schweißausbrüche. 😉

    Wenn das Forum Syntax-Highlighting für Ada hätte wärs vielleicht besser. 😃

    Es geht aber auch eher um die Möglichkeiten, die Enums dort bieten.

    Wobei, die Syntax ist anscheinend deutlich Zugänglicher als die von C++. Zumindest kommen Praktikanten, die ich recht häufig betreue, deutlich schneller mit Ada als mit C++ zurecht (sofern sie nicht schon vorbelastet sind).



  • Verdirb die Jugend doch nicht schon so früh! 😃



  • Fellhuhn schrieb:

    Da ist es wohl am einfachsten die gewünschen Enums in einer extra Datei zu verwalten und sich von einem Skript zusammenschustern zu lassen.

    Naja, je nachdem ist eine Implementierung in C++ gar nicht so kompliziert. Sobald man zum Beispiel auf Compilezeit-Sicherheit verzichtet, hat man bereits sehr viele Möglichkeiten. Mit dem dynamischen Skript hätte man diesen Nachteil ohnehin auch.



  • Nexus schrieb:

    Vielleicht sollten wir uns darüber unterhalten, welche genauen Anforderungen an die Enumeration gestellt werden. Die fett geschriebenen wurden bisher genannt, ich habe ein paar weitere mögliche aufgelistet:

    • Keine Codeduplizierung bei der Definition
    • Konvertierbarkeit zu Strings
    • Implizite/Explizite Konvertierbarkeit (von und) zu Ganzzahlen
    • Compilezeitfehler bei Zugriff auf nicht existenten Enumerator
    • Iteration durch alle Enumeratoren
    • ...

    Je mehr Kriterien erfüllt werden müssen, desto mühsamer wird natürlich eine Implementierung. Praktisch wäre auch ein vollständiges (und dennoch möglichst kleines) Beispiel, welches zeigt, was das enum alles leisten soll.

    Ich hab jetzt mal nochwas gebastelt, was alle Kriterien erfüllt, außer Konvertierung von einer Ganzzahl (geht nur, indem man über alle Werte iteriert) und außerdem ermöglicht, dass man nachträglich neue Werte hinzufügt (ich könnte mir vorstellen, dass das manchmal ganz nützlich ist). Nachteil ist, dass der Name als std::string bei einer Parameterübergabe kopiert wird, das könnte man durch const char* vermeiden, man hat dann aber nicht mehr die string -Komfortabilität.

    Lauffähiges Beispiel:

    #include <limits>
    #include <string>
    #include <vector>
    #include <iostream>
    #include <boost/scoped_ptr.hpp>
    
    template<typename T>
    class enum_entry
    {
    public:
    
    	typedef std::vector<enum_entry<T>*> container;
    
    	enum_entry() {}
    
    	enum_entry(std::string name, int value = start) : name(name), value(value)
    	{
    		start = value+1;
    
    		if(all_entries == NULL)    // Ehe jemand fragt: wenn ich all_entries nicht als Zeiger gemacht hab, kam es zu Abstürzen, offenbar wurde die statische Variable nicht vor dem Konstruktor initialisiert
    			all_entries.reset(new container);
    
    		all_entries->push_back(this);
    	}
    
    	std::string name;
    	int value;
    
    	static const container& all()
    	{
    		if(all_entries == NULL)
    			all_entries.reset(new container);
    
    		return *all_entries;
    	}
    
    private:
    	static int start;
    	static boost::scoped_ptr<container> all_entries;
    };
    
    template<typename T>
    int enum_entry<T>::start = 0;
    
    template<typename T>
    boost::scoped_ptr<typename enum_entry<T>::container> enum_entry<T>::all_entries(NULL);
    
    #define NAMED_ENUM(name) class name##_enum_impl {}; typedef enum_entry<name##_enum_impl> name; const name
    
    #define ENTRY(e) e(#e)
    #define ENTRY_NAME(e, name) e(name)
    #define ENTRY_VALUE(e, value) e(#e, value)
    #define ENTRY_NAME_VALUE(e, name, value) e(name, value)
    
    #define NEW_ENTRY(typ, e) const typ e(#e);
    #define NEW_ENTRY_NAME(typ, e, name) const typ e(name);
    #define NEW_ENTRY_VALUE(typ, e, value) const typ e(#e, value);
    #define NEW_ENTRY_NAME_VALUE(typ, e, name, value) const typ e(name, value);
    
    // Beispiel-Enums
    NAMED_ENUM(farbe) ENTRY(rot), ENTRY(blau), ENTRY_NAME(gruen, "grün");
    NAMED_ENUM(status) ENTRY(OK), ENTRY_VALUE(ERROR, -1), ENTRY_NAME_VALUE(CRITICAL, "critical error", -2), ENTRY(GOOD);
    
    // Später neuen Eintrag hinzufügen
    NEW_ENTRY(farbe, gelb)
    
    int main()
    {
    	using namespace std;
    
    	farbe f1 = rot, f2 = blau, f3 = gruen, f4 = gelb;
    	cout << f1.value << " " << f2.value << " " << f3.value << " " << f4.value << endl;
    	cout << f1.name << " " << f2.name << " " << f3.name << " " << f4.name << endl;
    
    	status s1 = OK, s2 = ERROR, s3 = CRITICAL, s4 = GOOD;
    	cout << s1.value << " " << s2.value << " " << s3.value << " " << s4.value << endl;
    	cout << s1.name << " " << s2.name << " " << s3.name << " " << s4.value << endl;
    
    	for(farbe::container::const_iterator i = farbe::all().begin(); i != farbe::all().end(); ++i)
    		cout << (*i)->value << ": " << (*i)->name << endl; 
    }
    

    Ausgabe:

    0 1 2 3
    rot blau grün gelb
    0 -1 -2 -1
    OK ERROR critical error GOOD
    0: rot
    1: blau
    2: grün
    3: gelb
    

    Kritik ist natürlich immer gern gesehen 😉

    Ach ja, noch ein Hinweis: da sich die enum_entry s nicht von selbst aus all_entries austragen, sollte man sie nur als globale (oder lokale statische) Variablen anlegen. Natürlich könnte man das noch hinzufügen, allerdings ist es nicht wirklich Sinn von Enums, dass man z.B. in Funktionen neue, temporäre Werte zaubert.



  • Sieht wirklich nett aus. Ich hatte was Ähnliches im Sinn, auch mit der Typgenerierung über Templates, allerdings waren meine Ansätze teilweise etwas weniger elegant. 🙂

    std::vector<enum_entry<T>* const>
    

    Geht das? Eigentlich müssen Elemente in STL-Containern kopierkonstruierbar und zuweisbar sein, was auf const -qualifizierte Objekte nicht zutrifft.

    Warum eigentlich __LINE__ ?

    Die Linkage dürfte zudem Schwierigkeiten bereiten, sobald man z.B. eine Enumeration im Header anlegt. Ich würde die Enumeratoren mit const versehen, dann hast du interne Linkage.

    Ein "Problem" ist auch noch, dass der Benutzer nachträglich Objekte hinzufügen kann. Dies kann erwünscht sein, allerdings wird die Enumeration sehr schnell unübersichtlich, wenn die Enumeratoren aus verschiedenen Stellen im Code zusammengetragen werden. Besonders wenn Header und Implementierungsdatei getrennt werden. Man könnte die entsprechenden Makros weglassen, allerdings wäre ein nachträgliches Hinzufügen prinzipiell immer noch möglich. Da hilft wohl nur die totale Obfuscation, sodass sich niemand mehr getraut, statt der Makros den direkten Code hinzuschreiben. 😃


Anmelden zum Antworten