enum class ohne enum Name verwenden

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

enum class ohne enum Name verwenden

Beitrag von Jonathan »

Sagen wir ich habe folgenden Code:

Code: Alles auswählen

enum class ZombieState {IDLE, WALK_AROUND};
ZombieState state;

switch(state)
{
case ZombieState::IDLE: // geht
	break;
case WALK_AROUND: // geht nicht :(
	break;
}
Wieso muss ich wenn ich eine Konstante aus dem enum verwende immer explizit dessen Namen angeben? Also, die Idee hinter enum class ist doch gerade, dass die im Gegensatz zu normalen enums typsicher sind. Natürlich könnte es verschiedene enum class mit gleich benannten Membern geben, aber dank Typsicherheit sollte das doch nie zu Problemen führen können. Insbesondere bei so direkten Zuweisungen oder Abfragen finde ich es doch sehr lästig.
Irgendwo geht für mich da der ganze Sinn des enums verloren, wenn ich immer den vollen Namen angeben muss. Da kann ich ja auch gleich globale Konstanten verwenden und mir die Doppelpunkte sparen. Die Idee sollte doch sein (wie bei namespaces und Klassen), das man jeweils nur so spezifisch sein muss, wie gerade nötig.
Also, mache ich hier einfach etwas falsch, oder ist C++ an dieser Stelle tatsächlich so doof? Und wenn ja, gibt es dafür einen vernünftigen Grund?
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: enum class ohne enum Name verwenden

Beitrag von Schrompf »

Wüsste nix, was da helfen könnte. Kann man den Enum vielleicht wie einen Namespace using'en?

Die Fallhöhe ist aber auch niedriger, wenn man vorher wegen vereinzelter Namenskonflikte die Angewohnheit entwickelt hat, jeden Wert eines Enums mit dem Enum zu beginnen:

Code: Alles auswählen

enum Bla { Bla_Blubber, Bla_Quiffel, Bla_Knoerpel }; 
Dann kann man das Vorwort weglassen und ist mit der neuen Methode quasi auf der selben Tippmenge, nur halt typsicher.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: enum class ohne enum Name verwenden

Beitrag von Krishty »

using enum ZombieState;

Beim Lesen der Dokumentation habe ich auch direkt gelernt, dass man enum class seit C++17 als typsichere beliebige Integer-Werte einsetzen kann:

Code: Alles auswählen

enum class FileDescriptor : int { };

FileDescriptor openLog() {
    return FileDescriptor{ open("/var/log/foo") }; // OK
}

int log = openLog(); // error: cannot convert from 'FileDescriptor' to 'int'
das ist awesome
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 575
Registriert: 05.07.2003, 11:17

Re: enum class ohne enum Name verwenden

Beitrag von Lord Delvin »

Im Prinzip ist das einfach ein Subtyp des Speichertyps hinter dem Doppelpunkt.
Ich würde bei enums immer den Speichertyp mit angeben. Ist ziemlich hässlich, wenn das fehlt und sich irgendwo irgendwer drauf verlässt.
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: enum class ohne enum Name verwenden

Beitrag von Krishty »

Ich bezog mich darauf, dass das ein Spezialfall der Initialisierungsregeln ist:

Code: Alles auswählen

enum byte : unsigned char {}; // byte is a new integer type
byte a = 42;         // error
byte b(42);          // error
byte c { 42 };       // OK as of C++17 (direct-list-initialization)
byte d = { 42 };     // error
byte e = byte{ 42 }; // OK as of C++17; same value as b
byte f { -1 };       // error
Extra, um solche Sachen wie Handles zu vereinfachen ohne dass man struct einsetzen muss.
Krishty hat geschrieben: 21.07.2019, 11:07 GIF zum Thema via http://mikelui.io/2019/01/03/seriously-bonkers.html

Bild
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: enum class ohne enum Name verwenden

Beitrag von Schrompf »

Oh cool, das wusste ich auch noch nicht. Das spart mir viele kleine struct SomeTypeSafeIndex { size_t idx; };
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: enum class ohne enum Name verwenden

Beitrag von dot »

Jonathan hat geschrieben: 07.10.2021, 13:55 Wieso muss ich wenn ich eine Konstante aus dem enum verwende immer explizit dessen Namen angeben? Also, die Idee hinter enum class ist doch gerade, dass die im Gegensatz zu normalen enums typsicher sind. Natürlich könnte es verschiedene enum class mit gleich benannten Membern geben, aber dank Typsicherheit sollte das doch nie zu Problemen führen können. Insbesondere bei so direkten Zuweisungen oder Abfragen finde ich es doch sehr lästig.
Irgendwo geht für mich da der ganze Sinn des enums verloren, wenn ich immer den vollen Namen angeben muss. Da kann ich ja auch gleich globale Konstanten verwenden und mir die Doppelpunkte sparen. Die Idee sollte doch sein (wie bei namespaces und Klassen), das man jeweils nur so spezifisch sein muss, wie gerade nötig.
Also, mache ich hier einfach etwas falsch, oder ist C++ an dieser Stelle tatsächlich so doof? Und wenn ja, gibt es dafür einen vernünftigen Grund?
Es gibt einen vernünftigen Grund: Namen wie IDLE in den globalen Scope zu leaken ist einfach eine schlechte Idee. enum class sind sogenannte "Scoped Enumerations". Wie der Name schon sagt, ist die Idee, dass die Namen der Enumerators eben nicht in den Parentscope leaken. Aber ja, implizite Konvertierungen gibt's bei denen auch nicht. Weil die sind auch eine schlechte Idee… 😉

Und nein, ein Satz globaler Konstanten definiert keinen eigenständigen Typ, ist also nicht wirklich vergleichbar.

Was dein Codebeispiel angeht, C++20 using enum to the rescue:

Code: Alles auswählen

enum class ZombieState { IDLE, WALK_AROUND };

ZombieState state;

switch(state)
{
using enum ZombieState;  // <-

case IDLE: // geht
	break;
case WALK_AROUND: // geht :)
	break;
}
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 575
Registriert: 05.07.2003, 11:17

Re: enum class ohne enum Name verwenden

Beitrag von Lord Delvin »

Wegen solchen Diskussionen mag ich die Community so :)
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: enum class ohne enum Name verwenden

Beitrag von Jonathan »

dot hat geschrieben: 08.10.2021, 15:55 Es gibt einen vernünftigen Grund: Namen wie IDLE in den globalen Scope zu leaken ist einfach eine schlechte Idee. enum class sind sogenannte "Scoped Enumerations". Wie der Name schon sagt, ist die Idee, dass die Namen der Enumerators eben nicht in den Parentscope leaken. Aber ja, implizite Konvertierungen gibt's bei denen auch nicht. Weil die sind auch eine schlechte Idee… 😉
Ich habe ein wenig mehr darüber nachgedacht, und mir fiel auf, dass das was ich mir wünsche tatsächlich recht anders funktionieren würde, als es Bezeichner in C++ normalerweise tun.
Natürlich will ich "IDLE" nicht im globalen Scope haben. Ich will es kontextabhängig automatisch auflösen. In einem Ausdruck, in dem an einer bestimmten Stelle nur ein ZombieState sinnvoll (d.h. kein Typfehler) wäre, sollte IDLE dann automatisch als ZombieState interpretiert werden. Klar, wenn es irgendwo ein Untertyp von int ist, wäre ggf. ein "auto state = IDLE + 5" auch ein möglicher Ausdruck, da den Typen herzuleiten ist natürlich irgendwie problematisch. Aber wenn man sich anschaut, was man mit Scoped Enumerations nunmal in der Praxis macht, dann sind das zu 98% simple Abfrage (state == IDLE) oder Zuweisungen (state = IDLE). Wenn für diese zwei Fälle Sonderregeln existieren würden, wären die Dinger schon signifikant besser zu verwenden. Das ist philosophisch betrachtet vielleicht hässlich, weil es ein Ausnahmefall ist und komplexere Ausdrücke direkt scheitern würden, Aber da könnte C++ halt einfach auch mal ein bisschen pragmatischer und benutzbarer sein. Sonderregeln sind vielleicht nervig für das Compilerteam, aber das war ja in C++ auch noch nie ein gültiges Argument :D
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: enum class ohne enum Name verwenden

Beitrag von Krishty »

Jonathan hat geschrieben: 08.10.2021, 20:02Natürlich will ich "IDLE" nicht im globalen Scope haben. Ich will es kontextabhängig automatisch auflösen. In einem Ausdruck, in dem an einer bestimmten Stelle nur ein ZombieState sinnvoll (d.h. kein Typfehler) wäre, sollte IDLE dann automatisch als ZombieState interpretiert werden. […] Wenn für diese zwei Fälle Sonderregeln existieren würden, wären die Dinger schon signifikant besser zu verwenden.
AFAIK hatte Cat mein Gejammer 2013 oder so zum Anlass genommen, ein Proposal für genau das zu schreiben. Vielleicht aber auch nur für case-Labels, denn dort kennt man den Typ ja definitiv durch den Typ im switch-Ausdruck. Ist offensichtlich nichts draus geworden, aber ich weiß nicht, warum.

(Dass ich bei switch(day) jedes Mal case Weekday::mondae schreiben muss statt schlicht case mondae, ist echt der Gipfel der Perversion. Bei int oder altem enum, okay. Aber bei enum class?! Diese Idioten)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 575
Registriert: 05.07.2003, 11:17

Re: enum class ohne enum Name verwenden

Beitrag von Lord Delvin »

Man könnte bei einem Nameresolution Error als Fallback nochmal einen closed lookup im expected Type machen.
*Allerdings* hat man in einer Sprache wie C++ natürlich das Problem, dass jeder Teil des letzten Satzes überall funktionieren muss.
Außerdem musst du den expected Type an der Stelle kennen. Das wird für einfache Fälle sicher der Fall sein. Ob man das im Allgemeinen auch kann, wäre jetzt eine Frage, die man irgendwen in der ISO WG fragen müsste und bekäme vermutlich trotzdem eine "muss ich prüfen" Antwort.
Die Regel mit nur switch wird einem vermutlich kaum helfen, weil switch/case mit anderen Kontrollflusselementen überlagern kann. Das wird daraus ein nichttriviales Problem machen.
Wenn ich das richtig im Kopf hatte, durfte man da sowas machen wie

Code: Alles auswählen

switch(x) { case A: if(b) { case B: } else {...}}
Zuletzt geändert von Lord Delvin am 10.10.2021, 09:02, insgesamt 1-mal geändert.
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
NytroX
Establishment
Beiträge: 358
Registriert: 03.10.2003, 12:47

Re: enum class ohne enum Name verwenden

Beitrag von NytroX »

Also ich denke, dass der Compiler in den meisten Fällen das enum Wissen könnte.
Im switch ist es logisch, weil man ja über einen bestimmten Typ switched.
Bei Funktionsaufrufen ist es aber auch der Fall.
Und bei der Initalisierung ebenso.

Bei anderen Fällen müsste man dann halt den Prefix verwenden, aber ehrlich gesagt fallen mir da nicht so viele Fälle ein.

Code: Alles auswählen

enum class ZombieState { IDLE, WALK_AROUND };
Zombie makeZombie(std::string name, ZombieState zstate);

switch(state){
	case IDLE: break; //kann man deducen, weil typeof(switch(state)) ist ZombieState
}

ZombieState state = IDLE; //ist kein assignment, sondern eine initialisierung, das weiß der Compiler
ZombieState state2(IDLE); //das auch
ZombieState state3 { IDLE }; //das auch
state = WALK_AROUND; //hier ist es kritisch
makeZombie("Zombie1", WALK_AROUND); //kann man deducen, weil typeof(arg2) ist ZombieState
auto state = ZombieState::IDLE //hier muss man es angeben, das kann der Compiler nicht wissen

aber mit using enum ist es ja schonmal ganz ok.
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 575
Registriert: 05.07.2003, 11:17

Re: enum class ohne enum Name verwenden

Beitrag von Lord Delvin »

NytroX hat geschrieben: 09.10.2021, 12:51 Also ich denke, dass der Compiler in den meisten Fällen das enum Wissen könnte.
Im switch ist es logisch, weil man ja über einen bestimmten Typ switched.
Bei Funktionsaufrufen ist es aber auch der Fall.
Und bei der Initalisierung ebenso.

Bei anderen Fällen müsste man dann halt den Prefix verwenden, aber ehrlich gesagt fallen mir da nicht so viele Fälle ein.
Also Template-Funktionen, bei denen das enum einen Template-Parameter als Typ verwendet in Verbindung mit Typinferenz, die C++ meines Wissens hat.

Außerdem ist Sprachdesign eine totale Funktion. D.h. es hilft nicht, dass der Compiler *meistens* irgendwas. Entweder immer oder nicht. Und das mit "dann halt Fehler" zu quittieren, kann in so komplexen Sprachen teils extrem merkwürdige Effekte haben.
Man sollte bei so Erweiterungen auch untersuchen, wie sich das auf die Qualität von z.B. Autocompletion in der IDE auswirkt.
Da ist gerade Java ganz groß drin, Spracherweiterungen zu bauen, die die Gesamtproduktivität einfach reduzieren :-/
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: enum class ohne enum Name verwenden

Beitrag von Jonathan »

Lord Delvin hat geschrieben: 10.10.2021, 09:08 Außerdem ist Sprachdesign eine totale Funktion. D.h. es hilft nicht, dass der Compiler *meistens* irgendwas. Entweder immer oder nicht. Und das mit "dann halt Fehler" zu quittieren, kann in so komplexen Sprachen teils extrem merkwürdige Effekte haben.
Man sollte bei so Erweiterungen auch untersuchen, wie sich das auf die Qualität von z.B. Autocompletion in der IDE auswirkt.
Da ist gerade Java ganz groß drin, Spracherweiterungen zu bauen, die die Gesamtproduktivität einfach reduzieren :-/
Ok, aber man könnte ja durchaus z.b. den Zuweisungs- und Vergleichsoperator erweitern. Anstatt dass auf der rechten Seite ein beliebiger Ausdruck stehen darf, könnte dann da auch alternativ ein einzelner enum-Name/Bezeichner ohne Scope stehen. Das wäre etwas hässlich, da es wirklich eine ganz explizite Ausnahme für Spezialfälle wäre, aber dafür hat man dann auch keine Probleme mit möglichen Nebenwirkungen 3 Stufen tiefer. Und es würde halt 95% der Use-Cases abdecken. Vermutlich könnte man mit etwas mehr Mühe auch noch ein paar andere nützliche Fälle abdecken (z.B. bedingte Initialisierung mit dem ? Operator), so dass fast alles was noch übrig bleiben würde, eh nicht als sauberer oder schöner Code gelten würde.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 575
Registriert: 05.07.2003, 11:17

Re: enum class ohne enum Name verwenden

Beitrag von Lord Delvin »

Ich glaube es wäre einfacher generell als Fallback in den Expectedtype zu schauen, wenn man den kennt.
Zuweisungs- und Vergleichsoperator gerade in C++ wird so schon kein Spaß sein.

Ehrlich gesagt habe ich mir kurz überlegt, ob ich es in Tyr einbauen würde, um zu schauen, wie es sich anfühlt.
Abgesehen davon, dass ich noch keine Enumerationen habe, glaube ich nicht, dass es wirklich gut funktioniert.
Wenn, dann mache ich das nur in switch als lokale Lösung und nicht generell.
Meine Syntax ist aber auch viel einfacher.
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Antworten