switch case für einen string
-
@5cript
Switch auf Strings ist halt etwas das in C++ so gar nicht einfach umzusetzen wäre weilstd::string
undstd::string_view
keine eingebauten Typen sind. Klar, man könnte es irgendwie hinbiegen. Vermutlich wäre die optimale Lösung es ähnlich wie bei ranged basedfor
zu machen und zu sagen alles was passendehash(x)
undx == y
hat wird beiswitch
unterstützt.Das wäre natürlich ein cooles Feature.
-
@hustbaer
Naja range based for funktioniert ja auch mit begin und end Konventionen.
Prinzipiell sehe ich nicht das Problem dass jemand cleveres sich was cleveres ausdenkt
Also, wem zwingen wir jetzt auf das Proposal zu schreiben mit allem drum und dran?
-
@5cript sagte in switch case für einen string:
Also, wem zwingen wir jetzt auf das Proposal zu schreiben mit allem drum und dran?
Ich kann leider gar niemanden zwingen irgendwas zu tun. Aber ich kann ja mal @Columbo hier taggen. Im sehr unwahrscheinlichen Fall dass es ihn ausreichend interessiert und er genug Zeit übrig hat... ich würde ihm auf jeden Fall zutrauen das hinzubekommen.
-
Ich möchte hier mal nach dem Usecase für ein String-Switch fragen.
Meistens gibt es bessere Lösungen wie z.B. eine Map (oder etwas mapartiges), wo man von dem String auf die gewünschte Funktionalität zeigt.
Erlaubt man beliebige Klassen, dann wird man auch kaum feststellen können, wann ein neues "string-enum"-Element hinzugefügt wird und an welchen Stellen überall ein
case "Neu"
fehlt - ich glaube also, dass dies daher auch fehleranfällig wäre.Was ich stattdessen viel lieber hätte, wäre eine
enum_java_style
-Klasse. Ich stelle mir das wie eine normaleenum class
vor, die aber die Funktionento_string
undvalue_of
hat - hat so ein bisschen Java-Style enums (also ggf. auch mit weiteren Member-Variablen/Funktionen). Die fehlen mir nämlich in C++ und die würden wahrscheinlich auch die Probleme der anderen Poster hier lösen.
-
Schau mal hier Magic enum
-
@wob
Beiswitch (string)
geht es eher umfrom_string
und nicht umto_string
/value_of
.Und
from_string
ist halt fummelig zu implementieren wenn es performant sein soll. Speziell wenn man das static initialization order fiasco vermeiden will/muss.Natürlich kann man das alles manuell ausprogrammieren. Genau so wie man manuell Funktoren schreiben kann ohne Lambdas. Macht aber keinen Spass und ist ein Haufen Boilerplate Code der nicht nötig wäre.
Ein
switch (T)
das mit allen Typen funktioniert die Hashable und Equality-Comparable sind wäre da schon nett.Wie oft man es wirklich braucht ist natürlich die Frage. Wenn man low-level Zeugs schreibt wie performante Parser für textbasierte Formate braucht man es aber schon eher oft.
-
Warum sollte man da eigentlich über eine Hastable gehen?
switch (my_string) { case "Wolf": do_a(); break; case "Hustbär": do_b(); break; default: do_default(); }
Hashtable scheint sich doch nur zu lohnen, wenn man richtig viele verschiedene Werte hat - die Hash-Berechnung ist ja schließlich nicht kostenlos und ein == ist häufig recht billig. Erfahrungsgemäß kommt "wenige Fälle" häufiger vor als "viele Fälle". Und wenn du jetzt doch sehr viele cases hast, sodass sich ein Hash ggf. lohnen würde, ist dann nicht die Wahrscheinlichkeit hoch, dass du die case-Werte sowieso irgendwo in einer Datenstruktur zur Verfügung hast und daher gar kein switch/case verwenden würdest?
-
@wob
Es wäre gut zu wissen wie es in JavaScript umgesetzt ist (zB V8 Engine) oder anderen Sprachen die das erlauben.
Ist kein 100% Apfel zu Apfel vergleich, aber die haben sie damit auch schon beschäftigt.Wär auch ein gutes Thema für ein blogpost mit Performance Tests.
-
@hustbaer sagte in switch case für einen string:
@5cript sagte in switch case für einen string:
Also, wem zwingen wir jetzt auf das Proposal zu schreiben mit allem drum und dran?
Ich kann leider gar niemanden zwingen irgendwas zu tun. Aber ich kann ja mal @Columbo hier taggen. Im sehr unwahrscheinlichen Fall dass es ihn ausreichend interessiert und er genug Zeit übrig hat... ich würde ihm auf jeden Fall zutrauen das hinzubekommen.
https://cplusplus.github.io/EWG/ewg-active.html#71
Ich denke, die Idee ist sinnvoll. Ich schreib mal den Autor an.
-
@wob sagte in switch case für einen string:
Hashtable scheint sich doch nur zu lohnen, wenn man richtig viele verschiedene Werte hat - die Hash-Berechnung ist ja schließlich nicht kostenlos und ein == ist häufig recht billig.
I see your point.
Für die Performance wäre es vermutlich gut wenn nicht
std::hash
verwendet würde sondern irgendwas billigeres. Für Strings kannst du z.B. einfach die Länge oder sowas wiesize() ^ back() << 16
nehmen. Das ist ausreichend billig zu berechnen und sollte schon einiges bringen. D.h. man bräuchte eine eigenenswitch_hash
Funktion oder sowas in der Art.Und vermutlich wäre es auch gut wenn diese spezielle Switch-Hash Funktion optional wäre, ohne Fallback. D.h. wenn es keine gibt, dann muss der Compiler halt linear-scan machen.
Man könnte auch überlegen zusätzlich eine
switch_id
Funktion zu erlauben, mit der Semantik dassswitch (x) { case X1: ...
gleichbedeutend ist mitswitch (switch_id(x)) { case switch_id(X1): ...
. Das wäre gut für kleine Klassen die komplett & verlustfrei in einen Integer serialisiert werden können. z.B. so Zeugs wiestd::chrono::duration
.
-
Ich wusste gar nicht, dass das fehlende String-Switch so ein häufiges Problem ist. Ist das wirklich so?
Vielleicht bin ich nur einfach in der falschen Branche tätig.
-
Es waere nicht verkehrt, es als Feature der Sprache zu unterstuetzen. Die Tatsache, dass es noch nicht aufgenommen wurde, bezeugt ja, dass die Nachfrage nicht so riesig ist. Aber ich sehe schon viele Faelle in unserer code base wo wir einen String auf mehrere fixe Werte hin pruefen.
-
@Columbo sagte in switch case für einen string:
Es waere nicht verkehrt, es als Feature der Sprache zu unterstuetzen. Die Tatsache, dass es noch nicht aufgenommen wurde, bezeugt ja, dass die Nachfrage nicht so riesig ist. Aber ich sehe schon viele Faelle in unserer code base wo wir einen String auf mehrere fixe Werte hin pruefen.
Sind das dann auch Fälle, wo das wirklich unbedingt nötig ist? Also bei mir ist das Hauptsächlich bei der Auswertung von Konfigurationsdateien der Fall, aber ab da wird im Grunde alles mit enums oder anderweitig behandelt.
-
Stringvergleiche sind halt typischerweise Fälle, wo die Werte weder hardcoded sein sollten, noch wo man die Zusatzperformance eines low-level Jumptables bräuchte. Die primäre Semantik von
string
ist "Zeichenkette mit menschenverständlichem Inhalt", nicht "Sprunglabel". Die primäre Semantik vonswitch
ist "Hochsprachliche Umsetzung dieses speziellen Assemblerkonstrukts, das super-wichtig war, als C neu war", nicht "Mapping von Werten zu Aktionen". Für letzteres ist halt diemap
das primäre Konstrukt, die auch ganz natürlich mit dynamischen Strings zurecht kommt. Von daher wundert mich nicht, dass das wenig Priorität für die Sprache hat.Gewiss wäre der Großteil der Fälle, wo Leute das anwenden möchten, eher Anti-Patterns, die Nutzereingaben mit Codeinterna verknüpfen möchten. Von dem Beispiel hier sehen wir nur sehr wenig, aber allein schon die zwei Zeilen mit dem "male" und "female" riecht danach, als ob hier Business Policy fest in Code gegossen werden soll, was generell eine schlechte Idee ist.
-
Patter matching würde das Problem mit erschlagen.
-
@It0101 sagte in switch case für einen string:
Ich wusste gar nicht, dass das fehlende String-Switch so ein häufiges Problem ist. Ist das wirklich so?
Vielleicht bin ich nur einfach in der falschen Branche tätig.Ich finde schon, nicht ständigt aber hin und wieder mal. Und ich finde es ist schneller und simpler sichtbar was passiert im Vergleich zu zum Beispiel einer erstellten Datenstruktur wie unordered_map.
@manni66 sagte in switch case für einen string:
Patter matching würde das Problem mit erschlagen.
Interessantes Proposal, die Syntax und das Feature sieht sehr mächtig aus, was mir die Sorge macht dass es sich eher schwerfällig durch den Review Prozess bewegen wird.
Ich kann mir auch nicht vorstellen, dass die Working Group nicht nochmal über die Syntax diskutiert etc.
-
@SeppJ sagte in switch case für einen string:
Stringvergleiche sind halt typischerweise Fälle, wo die Werte weder hardcoded sein sollten, noch wo man die Zusatzperformance eines low-level Jumptables bräuchte. Die primäre Semantik von
string
ist "Zeichenkette mit menschenverständlichem Inhalt", nicht "Sprunglabel". Die primäre Semantik vonswitch
ist "Hochsprachliche Umsetzung dieses speziellen Assemblerkonstrukts, das super-wichtig war, als C neu war", nicht "Mapping von Werten zu Aktionen". Für letzteres ist halt diemap
das primäre Konstrukt, die auch ganz natürlich mit dynamischen Strings zurecht kommt. Von daher wundert mich nicht, dass das wenig Priorität für die Sprache hat.Gewiss wäre der Großteil der Fälle, wo Leute das anwenden möchten, eher Anti-Patterns, die Nutzereingaben mit Codeinterna verknüpfen möchten. Von dem Beispiel hier sehen wir nur sehr wenig, aber allein schon die zwei Zeilen mit dem "male" und "female" riecht danach, als ob hier Business Policy fest in Code gegossen werden soll, was generell eine schlechte Idee ist.
Amen Bruder!
-
Oder man nimmt Pointer auf Strings. Müssen dann halt auch exakt die selben Pointer sein und nicht unterschiedliche Pointer auf gleiche Strings. Aber wofür gibts String Pooling.
-
@SeppJ sagte in switch case für einen string:
Stringvergleiche sind halt typischerweise Fälle, wo die Werte weder hardcoded sein sollten, noch wo man die Zusatzperformance eines low-level Jumptables bräuchte.
Typischerweise vielleicht. Gibt aber auch Leute die Parser schreiben.
(Und ich glaube in dem Satz fehlt ein "weder". EDIT: Sorry, Brainfart, steht eh da, bloss nicht da wo ich es erwartet hatte.)Die primäre Semantik von
string
ist "Zeichenkette mit menschenverständlichem Inhalt"Gewagte Aussage. Davon abgesehen:
human_readable
impliziert nicht!machine_readable
., nicht "Sprunglabel".
False dichotomy.
Die primäre Semantik von
switch
ist "Hochsprachliche Umsetzung dieses speziellen Assemblerkonstrukts, das super-wichtig war, als C neu war", nicht "Mapping von Werten zu Aktionen".Ebenso gewagte Aussage. Denn die Semantik von
switch
ist ganz exakt "Mapping von Werten zu Aktionen". Weil das genau das ist wasswitch
macht. Was willst du sonst nehmen?if
-Kaskaden?map<ValueType, std::function<...>>
? Bitte nicht.Für letzteres ist halt die
map
das primäre Konstrukt, die auch ganz natürlich mit dynamischen Strings zurecht kommt.map
soll das primäre Konstrukt sein um Werten auf Aktionen abzubilden? Was soll der Vorteil gegenüberswitch
sein? Dass man einen Haufen Boilerplatecode schreiben muss? Dass Callstacks unübersichtlicher werden?
-
Außer für
enum
wüsste ich auch keinen sinnvollen Anwendungsfall fürswitch...case
. Alles andere sehe ich als "Magic Numbers" (bzw. "Magic Literals").
Es ist für mich eindeutig ein "Code Smell" Funktionen mit langenswitch...case
-Blöcken zu haben - daher präferiere ich dann auch lieber, wie @SeppJ, Mapping-Datentypen.Es widerspricht ja auch gängigen OOP-Prinzipien.
Ich habe mal ein größeres Projekt (mit ein Dutzend Entwicklern) gesehen, da wurden Debug-Konsolenausgaben in einer Datei (weil nur dort Zugriff auf die "virtuelle" Konsole innerhalb einer 3D-Anwendung war) mithilfe einen großes
switch...case
-Blocks mit mehreren Hundert Zeilen abgehandelt)...