switch-Ausdruck des Typs 'std::string' nicht zulässig
-
@otze: doch, tut es. hab es selbst getestet, geht hervorragend!!! und ist noch dazu sehr einfach, wenn es erstmal konvertiert ist muss mans ja nicht in strings vergleichen.
mfg,
andi01.
-
dann schau mal meinen Edit. (und den zeitpunkt deines letzten Edits und meines Posts.)
-
jo, hab ich auch grade gesehen. Um das ganze ein wenig abzukürzen: beide methoden sind voll funktionstüchtig und klappen einwandfrei, welche er wählen will muss er selber wissen.
jedenfalls muss er es (egal mit welcher der beiden methoden) konvertieren bevor er es vergleichen kann.
mfg,
andi01.
-
Was sind "beide Methoden"? Wenn du deine meinst: Nein, tun sie nicht. Beide. Die erste Methode scheitert daran, dass die Strings keine Zahlen darstellen, die andere ist definitiv nicht in der Lage, das Problem zu lösen - wie auch, dein Code verändert nichts. Zumal du auf das Hauptproblem, den Switch selbst, nicht eingegangen bist.
-
man kann das ganze auch anschließend einfach mit if-bedingungen lösen, dann könnte man meine methode durchaus anwenden, muss ja nicht unbedingt ein switch sein. wenn man einen switch verwenden will kann man die kommandos theoretisch sogar in zahlen umwandeln (help=1; switch(command) ...), das würde aber nur unnötig aufwand bedeuten.
mit beide methoden meine ich:
1. er kann es direkt mit if-bedingungen lösen(z.B. if command=="help")("deine" methode).
2. er kann es(wenn es denn unbedingt ein switch sein muss) auch erst nach int konvertieren (zB durch ersetzen: help=1, exit=2,...) und dann seinen switch verwenden ("meine" methode).beides funktioniert.
mfg,
andi01.
-
andi01 schrieb:
mit beide methoden meine ich:
1. er kann es direkt mit if-bedingungen lösen(z.B. if command=="help")("deine" methode).
2. er kann es(wenn es denn unbedingt ein switch sein muss) auch erst nach int konvertieren (zB durch ersetzen: help=1, exit=2,...) und dann seinen switch verwenden ("meine" methode).zu 1. Nein, das war nur ein "könnte man machen, wenn sich etwas richtiges nicht lohnt". Meine Lösung kam danach(Der Codeblock)
zu 2. Wenn das deine Methode sein sollte, dann solltest du noch einmal in dich gehen, und das Problem betrachten, dann deine Lösung durchschauen und überlegen, was dein Code an dem Problem überhaupt ändert.Die Antwort darauf ist: herzlich wenig. Das geht mit nem Stringstream nämlich gar nicht. dafür brauchst du eine Lookup-table. Siehe meine map Lösung. Ersetze den Funktionszeiger durch int, dann hast du das, was du eventuell erreichen wolltest.
-
meine lösung würde ungefähr so aussehen:
string command; cin>>command; stringstream sstr; int command_neu; sstr<<command; sstr>>command_neu; if(command=="help") { command_neu=1; } if(command=="exit") { command_neu=2; } switch(command_neu) { case 1: cout<<" Hilfe ausgeben"\n"; break; case 2: //progamm beenden break; } }
aber die würde ich nur nehmen wenn es unbedingt ein switch sein muss, ansonsten gleich if-bedingungen.
mfg,
andi01.
-
andi01 schrieb:
string command; cin>>command; //Welchen Nutzen hat das hier für das Programm? stringstream sstr; int command_neu; sstr<<command; sstr>>command_neu; //Wenn du hier die if-kaskaden machst, dann brauchst du den Switch //dahinter nicht mehr. if(command=="help") { //dann könnte hier schon die Hilfe stehen command_neu=1; } if(command=="exit") { //und hier wird das Programm beendet command_neu=2; } //und das wird unnötig switch(command_neu) { case 1: cout<<" Hilfe ausgeben"\n"; break; case 2: //progamm beenden break; } }
-
Wenn ich den Befehl unbedingt in eine Ganzzahl für ein switch umwandeln wollen würde, dann würde ich ein Array (vector, was auch immer) von Befehlen erstellen, den String in diesem Array suchen und den Index des gefundenen Elements als Nummer nehmen.
Damit wäre ich aber nicht im mindesten zufrieden, weil die Nummer zu einer Magic Number verkommt, denn man hat keine offensichtliche Zuordnung zwischen Nummer und Befehl. Um das, was daraus folgt abzukürzen, letztlich würde es (bei mir) auf eine Map Befehl auf Funktor hinauslaufen, womit switch wieder hinfällig wäre.
-
LordJaxom schrieb:
Damit wäre ich aber nicht im mindesten zufrieden, weil die Nummer zu einer Magic Number verkommt, denn man hat keine offensichtliche Zuordnung zwischen Nummer und Befehl.
if(...) m["Help"]=CMD_HELP; m["Exit"]=CMD_EXIT; m["Menu"]=CMD_MENU; else m["Hilfe"]=CMD_HELP; m["Raus"]=CMD_EXIT; m["Menü"]=CMD_MENU;
-
volkard schrieb:
if(...) m["Help"]=CMD_HELP; m["Exit"]=CMD_EXIT; m["Menu"]=CMD_MENU; else m["Hilfe"]=CMD_HELP; m["Raus"]=CMD_EXIT; m["Menü"]=CMD_MENU;
Letztlich geht es bei Design ja darum, Wartbarkeit zu erhöhen und Arbeit zu verringern.
Hier müsste man für weitere Befehle an mindestens drei Stellen ansetzen, bei den Konstanten, beim Füllen der Map und bei der Auswertung im Switch. Da sowieso schon eine Map vorliegt, würde ein Speichern von Funktionszeigern (statt IDs) einen dieser Schritte eliminieren, da man nur noch beim Füllen der Map eine Funktion ergänzen muss. Zudem würde ein Schritt "abgesichert", da ein Vergessen der Definition dieser Funktion zu einem Compile- oder Linkerfehler führt, statt im switch einfach nur in den default-Zweig zu laufen (Laufzeitfehler oder garkein Fehler).(Das war der Schritt, den ich im letzten Post abgekürzt habe)
Das aber nur der Vollständigkeit halber.
-
also ich habe die Funktion "SwitchOnCommand" jetzt so abgeändert:
void SwichtOnCommand(std::string Command) { std::stringstream str_Command; char c_Command[250]; str_Command << Command; str_Command >> c_Command; switch(c_Command) { case "help": std::cout << "Bitte geben sie einen der folgenden Befehle an:" << '\n'; std::cout << "help - Zeigt eine Auflistung verschiedener Befehle" << '\n'; std::cout << "exit - Beendet die Anwendung" << '\n'; return; case "exit": Beenden = true; return; } std::cout << "Bitte geben sie einen gültigen Befehl ein..." << '\n'; }
allerdings will Switch bei mir nicht mal char-Arrys benutzen:
error C2450: switch-Ausdruck des Typs 'char[250]' nicht zulässig
Hab ich was falsch initialisiert oder funktioniert das auch nicht?
MFG
Neokil
-
DeepCopy schrieb:
Die switch-Anweisung erwartet einen skalaren/ordinalen Typ (ganzzahlig) also char, short, int, long usw.
-
Da merkt man mal,
a) wie ein einzelner "engagierter Halbwissenverbreiter" mehr bewirkt kann als ein ganzer Haufen von Experten wieder reparieren kann und
b) dass nicht wenige Fragende den Thead (erstmal? nur?) nach abtippbarem Quellcode scannen. Erklärungen ist Schall und Rauch... (oder zu schlecht/anspruchsvoll formuliert)Mein Vorschlag an andi01: Lösche Deine Beiträge hier im Thread, bevor noch mehr Ahnungslose in diese Falle tappen. Dein Tipp zur Konvertierung nach char[] hat gar nichts mit dem Problem zu tun (die if/else-Kaskade hätte hilfreich sein können, wenn Du sie nicht mit dem char[]-Zeug vermischt hättest).
Gruß,
Simon2.
-
Ok, noch einmal ganz langsam.
Was macht switch? Switch vergleicht einfache Zahlenwerte und vergleicht anhand dieser ob ein case-Zweig zutrifft. Der Grund, weswegen nur Ganzzahlen erlaubt sind ist, dass dies unglaublich effizient implementiert werden kann.
Nun hast du einen String. Ein String ist ein Array aus ganz vielen einzelnen Zeichen, und damit ziemlich weit von einer einzelnen Zahl entfernt. Deswegen kann man einen String nicht im Array verwenden. Hierbei ist es egal, ob du einen std::string oder einen char* oder einen char[256] verwendest. Es sind alles nichts weiteres als Felder. Auch std::string macht da nichts anderes. Der einzige Unterschied ist, dass string einfacher zu verwenden ist.
Deswegen ändert sich nichts, wenn man so etwas schreibt:
std::stringstream str_Command; char c_Command[250]; str_Command << Command; str_Command >> c_Command;
Denn damit verändert man nur die Repräsentation. Der Code oben ist übrigens gleich bedeutend mit
const char* cArray=Command.c_str();
Was der Code macht ist nichts anderes als die Repräsentation des Strings zu ändern. Dadurch ändert sich aber nichts an dem Problem, dass ein String ein Array von ganz vielen Werten ist, und ein Switch nur einen einzelnen Wert erwartet.
Wie löst man also das Problem richtig?
Es wird eine Art Tabelle gebraucht, die jeden gültigen Befehl eine Zahl zu ordnet. Wenn wir diese Zahl haben, können wir den Switch verwenden.
Mit Tabelle meine ich sowas:
Befehl->Zahl help->0 exit->1 foo ->2 bar ->3
Diese Tabelle kann man auf verschiedene Art und Weise implementieren. Am besten ist aber eine Map, da sie äußerst schnell Elemente suchen kann. Dies sähe dann so aus:
std::map<std::string,int> tabelle; //Befehl/Zahl paare hinzufügen tabelle["help"]=0; tabelle["exit"]=1; tabelle["foo"]=2; tabelle["bar"]=3; //Benutzer gibt Befehl ein: std::string command; std::cin>>command; //Nun müssen wir die Werte aus der Tabelle holen. Das Problem ist, nicht alles, //was der User eingibt korrekt ist, also müssen wir überprüfen, ob die Eingabe //Überhaupt gültig ist. Dazu wird find verwendet: std::map<std::string,int>::iterator tabellenEintrag=tabelle.find(command); //ist der Befehl ungültig, also nicht in der map enthalten, dann hat //tabellenEintrag einen ungültigen Wert. dieser ist genau spezifiziert und kann //mit einem Vergleich mit end() abgefragt werden: if(tabellenEintrag==tabelle.end()) { //wenn wir hier sind, war die Eingabe nicht gültig. std::cout<<"Ich spreche kein Klingonisch!"<<std::endl; } else { //Alles okay! Wir können nun den Tabelleneintrag lesen und die Zahl abfragen int nummer=tabellenEintrag->second; switch(second) { case 0: std::cout<<"Dies ist die Hilfe"<<std::endl; break; case 1: //... } }
Nun ist eine Map manchmal eine Dicke Kanone auf einen ziemlich kleinen Spatzen. In dem Fall ist man mit If-Abfragen besser aufgehoben, das ist die "faule" Version einer Tabelle:
if(command=="help") { std::cout<<"Dies ist die Hilfe"<<std::endl; } else if(command =="exit") { //... } else std::cout<<"Ich spreche kein Klingonisch";
Wenn man nun richtig richtig viele Befehle hat, dann ist man auch irgendwann mit dem Switch überfordert. Jedes mal wenn man einen neuen Befehl hinzufügt, müsste sowohl die Tabelle als auch der switch verändert werden. das kann schnell ziemlich unübersichtlich werden. Irgendwann wird irgendjemand die falsche Zahl an ein Label schreiben und dann darf man wieder lustig Debuggen.
In dem Fall bieten sich Funktionszeiger an, und dies war die zuerst von mir angebotene Lösung. Hierbei speichern wir statt einer Zahl direkt einen Zeiger auf die Funktion, die wir aufrufen wollen, nachdem der Befehl eingegeben wurde. Dies ermöglicht es uns dann ganz einfach, diese Funktion direkt aufzurufen, anstatt uns durch einen Switch zu hangeln.
//Funktion, die die Hilfe ausgibt void printHelp() { cout<<"Dies ist die Hilfe"; } //in der Main map<string,void (*)()> commands; //füllen. Anstatt eines Zahlenwertes, wird die Funktion zugewiesen. commands["help"]=printHelp; std::string command; std::cin>>command; map<string,void (*)()>::iterator tabllenEintrag=commands.find(command); if(tabellenEintrag!=commands.end()) { //anstatt eines Switches nur noch ein Funktionsaufruf tabellenEintrag->second(); } else { cout<<"ungueltiger Befehl"; }
-
Also ich mache meine Kommando-auswertung immer so:
void ProcessCommand( std::string Command ) { if ( Command == "help" ) { ... return; } if ( Command == "foobar" ) { ... return; } }
Viel besser oder schlimmer als switch-case ist das auch nicht.... Wenn es natürlich 100 verschiedene Kommandos sind, wird es echt unästhetisch.
-
It0101 schrieb:
Also ich mache meine Kommando-auswertung immer so:
void ProcessCommand( std::string Command ) { if ( Command == "help" ) { ... return; } if ( Command == "foobar" ) { ... return; } }
Viel besser oder schlimmer als switch-case ist das auch nicht.... Wenn es natürlich 100 verschiedene Kommandos sind, wird es echt unästhetisch.
Diese Lösung ist ja auch nicht falsch sondern bei wenigen Befehlen sogar vollkommen angemessen.
Wenn du aber viele Befehle hast und auch oft welche hinzukommen, so bietet sich die von otze vorgeschlgene Lösung mit Funktionszeigern an.Übrigens kannst du anstatt
std::string Command
lieberconst std::string& Command
schreibenAußerdem wäre ein
else if
angemessener
-
also nochmal danke, aber ich habe jetzt das ganze mit der map gemacht.
Aber bei mir steht da immer:error C2039: 'map': Ist kein Element von 'std'
und noch einige andere Fehler, die (so weit ich das beurteilen kann) alle davon stammen, dass er 'map' nicht kennt. Muss ich da noch was includieren/verlinken?
MFG
Neokil
-
Übrigens kannst du anstatt std::string Command lieber const std::string& Command schreiben
bzw. kann man dann auch direkt eine const-Referenz nehmen, da wir ja nur auswerten und nicht ändern.
Außerdem wäre ein else if angemessener
warum? durch die return-struktur erfüllt meine Lösung doch den gleichen zweck, und sieht auch noch besser aus
-
Neokil schrieb:
error C2039: 'map': Ist kein Element von 'std'
#include <map>