enums mit Funktionen erweitern



  • Hallo zusammen

    Ich bin eben beim Design meiner Klassen awuf die Frage gestoßen, ob es nicht irgendwie möglich ist, ein enum mittels Funktionen zu erweitern.

    Nehmen wir mal an ich hab folgendes enum:

    enum class Direction {
        Up, Down, Left, Right
    }
    

    dann möchte ich beispielsweise folgendes tun können:

    Direction up = Direction::Up;
    bool h = up.isHorizontal();
    

    Ich kann natürlich hergehen und etwas in der Art schreiben

    class Direction {
       enum _internal { Up, Down, Left, Right };
    
       Direction(_internal dir) { ... }
    
       bool isHorizontal() { ... }
    }
    

    aber das geht nicht, wenn ich class-enums behalten will (was ich eigentlich will)

    Ich hoffe auf schlaue Ideem eurerseits 🙂

    Disclaimer:
    Bitte nicht zu sehr an dem obigen Beispiel aufhängen, das ist nur ein ganz vereinfachtes Beispiel, das verdeutlichen soll, worum es mir geht 🙂



  • Nein, du kannst enums nicht um Methoden erweitern.
    Eine Alternative wären aber auch freie Funktionen.

    bool h = isHorizontal(Direction::Up);
    


  • Ja, das ist auch mein aktueller Stand.

    Der Vorteil daran, wenn es direkt Teil der enums wäre, wäre ja der daß die Funktionen einfacher zu finden sind, bspw über die Codecompletion der IDE.

    Daß es nicht direkt geht ist klar. Aber vielleicht kennt ja noch jemand Tricks, mit denen man so tun kann als ob. So wie mein Beispiel einer Wrapper-Klasse um das enum von mir 🙂



  • Hallo,

    ich habe ne Zeit lang mit Java gearbeitet - und dort können die enums sowas. Ich habe die Java-Enums sehr häufig genutzt, die in C++ finde ich eher schwach, auch die C++11 enum class.

    Was du tun kannst, um das zu emulieren:

    //im Header
    
    class Direction {
       int value;
       Direction(int x) : value(x) {}
    public:
       boolean isHorizontal() const;
    
    public:
       static const Direction Up;
       static const Direction Down;
       static const Direction Left;
       static const Direction Right;
    }
    
    // im cpp
    
    const Direction Direction::Up(0);
    const Direction Direction::Down(1);
    const Direction Direction::Left(2);
    const Direction Direction::Right(3);
    

    Oder zumindest so ähnlich.
    Jedenfalls nervt es, dass valueOf und toString in C++ nicht automatisch da sind.

    Falls jemand ne bessere Lösung hat, immer her damit!



  • wob schrieb:

    Jedenfalls nervt es, dass valueOf und toString in C++ nicht automatisch da sind.

    C++ ist nun einmal eine Sprache ohne Reflektion und mit der Prämisse das man nicht Overhead durch nicht benötigte Details bekommt. Für toString und Co müsste es aber auf die Bezeichner zur Laufzeit zugreifen können oder diese bei der Kompilierung immer mit einbetten, ersteres erfordert nun einmal Reflektion, letzteres wäre häufig ungewünschter Overhead.

    Beide Sprachen haben ihre vor oder Nachteile. C++ ist halt eher für Ressourcenschonung und Performance ausgelegt, Java und Co liefern dafür Laufzeitinformationen mit allen Vor- und Nachteilen.



  • asc schrieb:

    C++ ist nun einmal eine Sprache ohne Reflektion und mit der Prämisse das man nicht Overhead durch nicht benötigte Details bekommt. Für toString und Co müsste es aber auf die Bezeichner zur Laufzeit zugreifen können oder diese bei der Kompilierung immer mit einbetten, ersteres erfordert nun einmal Reflektion, letzteres wäre häufig ungewünschter Overhead.

    Naja, man könnte auch ohne Reflektion Dinge wie Funktionen in einer enum class erlauben.

    Und zu dem zusätzlichen Overhead: man könnte es ja so machen, dass man explizit

    std::string toString() = default;
    

    in die Klasse schreiben muss, um diese Funktion autogeneriert zu bekommen. Wobei, vielleicht will man auch auch gleich operator<< und >> haben. Könnte man auch autogenerieren.

    Ist das wirklich so ein seltener usecase in C++? Ich hatte mal hierzu ein bisschen rumgesucht und nur irgendwelche obskuren Makrolösungen gefunden, die mir alle nicht gefallen haben.


  • Mod

    enum in C++ ist eher ein maschinentechnischer Begriff, als glorifizierter Integer.

    enum in Java ist eher ein informationstechnischer Begriff, als kategorischer Datentyp.

    Hinter beiden stecken ähnliche Ideen, aber bei C++ ist die Betonung eher, zu definieren, wie das hinterher im Maschinencode aussieht. Bei Java geht es eher darum, was der Datentyp können muss. Bei C++ ist es eben nicht so wichtig, wie die Kategorien heißen, da es darum geht, Integerkonstanten zu definieren. Bei Java sind die Kategoriebezeichner wichtiger Teil der Funktionalität, da es darum geht, einen kategorischen Datentyp zu definieren.



  • wob schrieb:

    Ich hatte mal hierzu ein bisschen rumgesucht...

    Ich sogar ein bisschen mehr, aber gefunden hab ich auch nichts.
    Boost hatte da mal was in der Richtung, was sich aber wohl auch nicht durchgesetzt hat.
    War auch schwer verwundert, als ich damals was von 'enum class' gehört habe und dann kam...naja mehr oder weniger nichts.



  • wob schrieb:

    Hallo,

    ich habe ne Zeit lang mit Java gearbeitet - und dort können die enums sowas.

    Ja, daher auch meine Idee sowas auch für C++ zu basteln 🙂

    Was du tun kannst, um das zu emulieren:

    ...
    

    Auf sowas in der Art will ich auch hinaus, eine schicke kleine Wrapperklasse um das enum, das die nötigen Funktionen implementiert. Aber letztlich hab ich dadurch wieder andere Nachteile, wie z.B. daß ich auf static-Variablen zurückgreifen muss oder auf die class enums verzichten muss.
    Bisher bin ich da noch auf keine Variante gekommen die wirklich besser sind als ein reines, nacktes enum 🙂

    Oder zumindest so ähnlich.
    Jedenfalls nervt es, dass valueOf und toString in C++ nicht automatisch da sind.

    Ist ja auch so ein klassischer Anwendungsfall den man gern irgendwie einfach lösen möchte. Hatte schon überlegt ob man dafür nicht irgendeine Art Präprozessor bauen kann... 🙂



  • enum schrieb:

    Oder zumindest so ähnlich.
    Jedenfalls nervt es, dass valueOf und toString in C++ nicht automatisch da sind.

    Ist ja auch so ein klassischer Anwendungsfall...

    Ist er das? Es gab bislang nur sehr, sehr wenige Fälle in denen ich dieses Feature unter C++ vermisst habe. Und bei diesen Ausnahmefällen (glaube 2-3 in meiner bisherigen Zeit als Softwareentwickler und ich mache dies schon 15-20 Jahre beruflich), habe ich mir mit freien Funktionen geholfen.



  • Naja, kommt halt drauf an was man macht.

    Ich hab z.B. viele Daten die ich einlesen muss, und muss da oft Strings aus irgendwelchen XML Attributen auf ein enum in meinen Datenstrukturen mappen.
    Wenn man dann ein paar dutzend solcher Mappings hat, wäre eine Automatisierung schon fein 🙂



  • asc schrieb:

    Ist er das?

    Ja. Deshalb haben das viele andere Sprachen und deshalb gibt es diese vielen mehr schlecht als recht Lösungen für c++ im Netz.



  • Wer nicht mit der Zeit geht, geht irgendwann. 😉

    Mal ernsthaft, meistens braucht man Reflection nicht. Dann gibt es tatsächlich Fälle, wo man diese braucht. Aber dann würde ich (je nach Aufwand) empfehlen, nicht C++ zu verdammen, sondern eine Scriptsprache einzubetten, die das übernimmt. Es sollte heute bei größeren und komplexen C++-Projekten doch möglich sein, sich Gedanken zu machen, ob man nicht manche Aufgaben mit einem Binding zu Lua, Python oder AngelScript komfortabler und schneller erledigen kann?

    Denn manchmal zählt die Performance nicht so stark. Und wenn man Reflection in C++-Klassen direkt selber einbaut, ist das auch nicht unbedingt schneller. Wahrscheinlich fehleranfälliger, weil man später die Eigenschaften einer Klasse ändert und dann vergisst das Fake-Reflection nach zu ziehen.



  • Keine Ahnung, wovon du redest...
    Niemand 'verdammt' C++, niemand will C++-Reflection.

    Lediglich enum class wird kritisiert, weil es 'neu' ist, aber für viele nicht das gebracht hat, was sie erhofft/erwartet haben.



  • Ich behelfe mir in solchen Fällen mit Codegenerierung. Ein einfaches JSON als Input und ein paar Zeilen Python o.ä. und schwups hat man eine portable Möglichkeit enums mit from/toString zu definieren. Kann einfach in den Buildprozess eingebunden werden und wenn ich mehr brauche erweitere ich einfach nur den Generator. Hat den Vorteil, dass es reines C++ ohne Makrogefummel produziert und man behält die Übersicht in eben solchen Fällen wie oben geschildert (zig enums aus Input-File, die sauber gemappt werden müssen). Eine Anbindung zu Skriptsprachen mit Reflection ist doch totaler Overkill, wenn es wirklich nur darum geht. Man denke nur mal ans Deployment und den Stress das über Jahre zu warten. Ganz davon abgesehen, dass man sich da gut und gerne mal verzehnfacht was die Binaries betrifft.



  • Ja, vielleicht werd ich in Zukunft doch mal mehr Codegenerierung in Betracht ziehen, wenn ich Daten aus Textdateien einlesen muss 🙂

    In meinem aktuellen Fall gings aber tatsächlich nur darum, statt

    isHorizontal(dir)
    

    zu schreiben, dies mit

    dir.isHorizontal()
    

    tun zu können, weil ich den Syntax so angenehmer finde, bzw. auch besser mit Codecompletion funktioniert.


Anmelden zum Antworten