"Extended" Scoped Enums und Flags



  • Hi! Da hier grad so wenig los ist, poste ich einfach mal mal einen Ansatz aus meiner Experimentierküche, um Scoped Enums (enum class ...) mit zusätzlichen Informationen und Funktionalität zu versehen. Vielleicht findet ihr den ja auch interessant oder sogar brauchbar.

    Eigentlich war ich ja auf der Suche nach einem guten Konzept, um enum-Werte als Text auszugeben (ein Klassiker, auch ich warte auf C++ Reflection 😉 ). Im Zuge meines DOS-Retro-Hobbyprojekts kam dann noch die Anforderung hinzu, dass ich Unterstützung für Bitweise Operationen mit Scoped "Enum Flags" haben und auch noch zusätzliche Daten an die Werte "anhängen" wollte.

    So hatte ich z.B. enum-Werte für digitale Audioformate wie z.B. PCM_44KHZ_STEREO_16BIT, wollte aber bei dessen Verwendung nicht jedesmal endlos lange if/else/switch-Konstrukte schreiben, wenn ich zu dem enum-Wert die zugehörige Frequenz oder die Anzahl der Kanäle als Integer ermitteln musste.

    Die Idee ist ein extended_enum_traits<T> struct, in das man die ganzen Informationen schön säuberlich in eine Tabelle einträgt. Dazu ein paar Hilfsfunktionen wie extended_enum_info(enum_value), womit man sich den entsprechenden Tabelleneintrag holen kann, concept-basierte Operatoren als freie Funktionen (z.B. für die bitweisen Operationen auf "Enum Flags") und eine generische std::formatter<T>-Implementierung um die enum-Werte als Text via std::format/std::print auszugeben.

    Ferner auch noch Funktionen, die solche "Enum Flags" zu einer Range machen, wodurch man die gesetzten Flags mit einem Range-Based-For durchlaufen oder sie mit std::ranges-Algorithmen verwenden kann (die klassischen std-Algos funktionieren leider nicht, da diese Klassen-Typen erfordern. Aber std::ranges` erlaubt auch nicht-Klassen-Typen wie Scoped Enums als Ranges).

    Der Ansatz ist auch ganz praktisch, um z.B. detaillierte Text-Fehlermeldungen an die enum-Werte zu hängen. Bei meinem DOS-Retroprojekt will ich z.B. oft Exceptions vermeiden und verwende gerne simple Fehlercodes zusammen mit std::expected (z.B. bei der IO-Port-Kommunikation mit der Soundkarte). So kann ich bei Bedarf die Fehlermeldungen abrufen und wenn ich sie nicht brauche ("referenziere"), dann optimiert der Compiler die heraus und sie landen am Ende auch nicht in der .exe-Datei.

    Hier meine derzeitige Implementierung auf Godbolt:

    https://godbolt.org/z/nx1M6fWW7

    Ab Zeile 396 die Anwendungsbeispiele. Die sind vielleicht interessanter als das ganze concept und operator-Geraffel.


Anmelden zum Antworten