1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen

Command Design Patterns -> mit User input

Dieses Thema im Forum "Programmieren" wurde erstellt von Sadakazu, 20 April 2017.

  1. Sadakazu

    Sadakazu Member

    Registriert seit:
    7 Juni 2016
    Beiträge:
    140
    Hallo....
    Ich hoffe das ich jetzt endlich das richtige Thema gefunden habe xD
    Also ich möchte das ein Benutzer mehrere Kommandos eingeben kann.

    zb: foo, bar, foobar, barfoo, start, stop, start foo, start bar, start foobar, start barfoo usw usf....

    Ergo möchte ich Kommandos mit einer Parameter Liste erstellen....
    Denkbar einfach wären ja strings Arrays zu benutzen und das ganze mit if else zu bauen....

    Bei zwei drei Kommandos kann man das ja durchaus noch realisieren.... Aber bei 10 Komandos und mehr?

    Kann man das mit den Command Design Patterns regeln?

    Ich hab auf linuxtopia.org dazu einen kurzen artikel gelesen...
    Hier mal dazu der Quellcode:
    Code:
    //: C10:CommandPattern.cpp
    #include <iostream>
    #include <vector>
    using namespace std;
    
    class Command {
    public:
    virtual void execute() = 0;
    };
    
    class Hello : public Command {
    public:
    void execute() { cout << "Hello "; }
    };
    
    class World : public Command {
    public:
    void execute() { cout << "World! "; }
    };
    
    class IAm : public Command {
    public:
    void execute() { cout << "I'm the command pattern!"; }
    };
    
    // An object that holds commands:
    class Macro {
    vector<Command*> commands;
    public:
    void add(Command* c) { commands.push_back(c); }
    void run() {
    vector<Command*>::iterator it = commands.begin();
    while(it != commands.end())
    (*it++)->execute();
    }
    };
    
    int main() {
    Macro macro;
    macro.add(new Hello);
    macro.add(new World);
    macro.add(new IAm);
    macro.run();
    } ///:~
    Soweit ist mir das ganze Thema auch klar
    Ich würd das ganze zwar nicht unbedingt in einer Datei schmeißen sondern Hello, World und IAm jeweils in eine eigene cpp und hpp datei schmeißen....

    mit macro.add(new Hallo); wird ja die Hallo klasse aufgerufen...
    wie könnte ich das aber bewerkstelligen das ein user etwas eingeben kann, und entsprechend die richtige Funktion aufgerufen wird...

    ich kann mir vorstellen das ich irgendwo auch noch die Kommandos "verfügbar" machen muss... also irgendwo "registrieren" muss, damit das Programm weiß, ah ja... diese und jenen Kommandos hab ich in meiner liste... also gebe ich das und das aus.. wenn ein falsches Kommando ausgegeben wurde dann geb ich halt ne Fehlermeldung zurück....

    Oder bin ich hier doch schon wieder an der falschen stelle?? -.-" ^^
     
  2. rubricanis

    rubricanis Homo ludens

    Registriert seit:
    22 August 2011
    Beiträge:
    658
    Ort:
    Ottendorf, Niederösterreich
    Dann musst du eine kleine REPL (read eval print loop) und cmd interpreter schreiben. In etwa so (Pseudocode):
    Code:
    
    bool quit = false;
    
    function repl(){
       str prompt = "> ";
       while ! quit {
          println(prompt);
          str s = readln();
          bool err = interpret(s);
          if err {
            println ("error message");
          }
       }
    }
    
    function interpret(str s) {
       if s == "quit" {
           quit = true;
           return false;
       }
       else if
           ....
           ....
       return true; // error
    }
    
    Das ist nur die Struktur, dir fällt bestimmt besserer code ein...
     
  3. KobRheTilla

    KobRheTilla used register

    Registriert seit:
    20 Januar 2011
    Beiträge:
    1.065
    Versuch doch erstmal die grundlegenden Ein- und Ausgabemechanismen zu verstehen.

    Rob
     
  4. rubricanis

    rubricanis Homo ludens

    Registriert seit:
    22 August 2011
    Beiträge:
    658
    Ort:
    Ottendorf, Niederösterreich
    Nachtrag: Vermutlich ist es besser deine Kommandos als ENUM zu codieren. Dann kannst du in interpret(s) einen assoziativen Array verwenden um das Kommando zu holen. Dann einen einfachen switch oder ein Index in einen Array. Klar, kommt darauf an auf was du hinaus willst. Aber einfach ist immer gut: weniger Laufzeit, weniger Code, weniger Fehler! :cool:
     
  5. Sadakazu

    Sadakazu Member

    Registriert seit:
    7 Juni 2016
    Beiträge:
    140
    Tja... wie mir scheint sind wohl doch nicht alle Grundlagen durchgearbeitet worden.....

    Und genau da ist wieder das Problem von der if else if verzweigung...

    bei 3 4 commands kann man das ja noch machen....

    Aber wenn man 10 Commands benutzen möchte? und im laufe der entwicklung kommen dann noch 10 dazu? und später will mans noch weiter erweitern und dann kommen nochmal 20 dazu?

    40 If else if verzweigungen sind da seeehr unübersichtlich :D
     
  6. thor-os

    thor-os New Member

    Registriert seit:
    21 April 2015
    Beiträge:
    26
  7. darktrym

    darktrym Fahnenträger

    Registriert seit:
    21 August 2006
    Beiträge:
    1.621
    Ort:
    Düsseldorf
    Gott hat den Programmierern Dictionaries geschenkt um IF-THEN-ELSE-Blöcke zu verringern.
     
  8. rubricanis

    rubricanis Homo ludens

    Registriert seit:
    22 August 2011
    Beiträge:
    658
    Ort:
    Ottendorf, Niederösterreich
    Ach, kein ernsthaftes Problem: Du wirst dem Benutzer kaum 40 oder mehr Kommandos zumuten, und falls doch dass vermutlich in Kommano/Subkommando/Options unterteilen. Ansonsten ist ein Dictionary (assoziativen Array) wie darktrym vorschlägt oder ein switch mit Konstanten/Enums dein Freund. Mach dich nicht vorzeitig verrückt, bring die Sachen doch immer erst einmal zum laufen damit du die Struktur verstehst, optimieren oder reorganisieren kannst du dann immer noch. No premature optimisation...

    Ich habe den Eindruck du denkst zu sehr TOP-DOWN, und nicht wie es besser ist (gerade am Anfang) BOTTOM-UP, - kleine, testbare Komponenten die du dann zusammenfügst. Wenn du einen kleinen command interpreter hast (wie immer der aussieht), dann kanst du deine anderen Komponenten leicht test und dann siehst du was funtioniert und was nicht.
     
  9. Sadakazu

    Sadakazu Member

    Registriert seit:
    7 Juni 2016
    Beiträge:
    140
    hm ich hatte das mit switch case so verstanden das nur integer werte in case funktionieren?
    Gut switch case würde natürlich vieles vereinfachen (langfristig aber trotzdem nicht das gelbe vom ei)...
    Aber geht das denn wenn ich schreibe:
    Code:
    switch(cmd) {
    case 'foo' :
      foo();
    break;
    case 'bar' :
      bar();
    break;
    // 10 weitere cases später
    default :
      fooBar();
    }
    
    Was genau meinst du damit?
     
  10. thor-os

    thor-os New Member

    Registriert seit:
    21 April 2015
    Beiträge:
    26
    Hast du es denn versucht? Meist geht es doch irgendwie und auch wenn die Lösung nicht optimal ist, funktioniert es erst einmal. Der Vorschlag von darktrym hat aber auch was. Also die Lösung über ein Dictionary ist aufwändig aber elegant.

    Schau mal hier zu deiner Frage:
    http://en.cppreference.com/w/cpp/language/switch
     
  11. Sadakazu

    Sadakazu Member

    Registriert seit:
    7 Juni 2016
    Beiträge:
    140
    Habs jetzt grad mal mit mehreren varianten versucht...
    Code:
    #include <iostream>
    #include <string>
    #include <sstream>
    #include "MainUtils/MainUtils.hpp"
    
    // Testfunktion als Command interpreter
    void testfunk(std::vector<std::string> command) {
    
       // zu testzwecken mal alles ausgeben
       for (int i = 0; i < command.size(); i++) {  // Warnung: Vergleich zwischen vorzeichenbehafteten und vorzeichenlosen Ganzzahlausdrücken Warum? o0
           std::cout << command[i] << std::endl;
         }
       // cin bereinigen
       std::cin.ignore();
    
       // switch case ..... Compiler Error -> Swicht-Größe ist keine Ganzzahl
       switch (command[0]) {
       case 'test' :   // Compiler Warung: Zeichenkonstante mit mehreren Zeichen
         std::cout << "es wurde test eingegeben" << std::endl;
         break;
       case 'foo' :
         std::cout << "es wurde foo eingegeben" << std::endl;
         break;
       default:
         std::cout << "befehl nicht gefunden" << std::endl;
       }
    }
    
    int main() {
    
        // Eigene utils für pause und co.....
       MainUtils::MainUtils util;
    
    
       std::vector<std::string> command(0);
       
       std::cout  << "> ";
       std::string cmd[3];
       std::string line;
       getline(std::cin, line);  // get the entire line
       // parse each string from the line
       std::istringstream stream(line);
      for (int i=0; stream.good(); i++) {
         stream >> cmd[i];
         command.push_back(cmd[i]);
       }
       std::cin.clear();
       std::cin.sync();
       testfunk(command);
    
       util.pause();
       return 0;
    }
    Hab die fehler kommentiert....
    Funktioniert so also nicht...
    auch wenn ich mit google nach switch case string suche... werden die strings immer in einen integer convertiert....
    Ooooookkkaaayyyy o0 Werd ich mal nach googeln :D
     
  12. rubricanis

    rubricanis Homo ludens

    Registriert seit:
    22 August 2011
    Beiträge:
    658
    Ort:
    Ottendorf, Niederösterreich
    Ich denke da liegst du falsch. Switches mit Enums sind sehr effizient da sie von heutigen Compilern gut optimiert werden können. Wenn du sie in einem zentralen Modul in zwei Funktionen packst (z.B. cmd_from_str(const char*), str_from_cmd(Cmd)) dann ist das sehr einfach, flexibel und effizient.

    Code untested, from top of my head.
    Code:
    #include <unordered_map>
    #include <stdexcept>
    
    enum class Cmd {
       foo, bar, baz
    }
    
    typedef std::unordered_map<const char*, Cmd> CmdMap;
    
    CmdMap map = {{"foo", Cmd::foo},{"bar",Cmd::bar},{"baz",Cmd::baz}};
    
    try {
       // input cmd von argv oder wo immer holen.
       Cmd cmd = map.at("cmdstr");
    
       switch(cmd) {
          case Cmd::foo : ... break;
          case Cmd::bar : ... break;
          case Cmd::baz : ... break;
       };
    }
    catch (const std::out_of_range& err) {...}
    Natürlich könntest du deine FuntionsPtr auch direkt in eine Map stecken, ich halte aber ein solches Design für flexibler. Wie immer: das kann man auch anders sehen.
    Ich will da gar nicht argumentieren, ich habe einfach den Eindruck dass du zu weit vorausdenkst und zu schnell eine Lösung für dein spezielles Vorhaben haben willst. InOut ist ein notorisch schwieriges Thema und damit sollt man sich auseinandersetzen bevor man in die Tiefen geht. Die Lisp-Leute sagen das in jedem ernsthaften Programm ein kleiner Lisp Interpreter steckt, und da haben sie recht.:)
     
    Sadakazu gefällt das.
  13. Sadakazu

    Sadakazu Member

    Registriert seit:
    7 Juni 2016
    Beiträge:
    140
    Ich muss mir enum glaub ich nochmal vorknöpfen....
    So ergibt das ganze natürlich sinn....
    Auch wenn ichs persönlich jetzt vermutlich ehr mit nem vector als string gelöst hätte o0
    Aber ich probier das mal aus....

    PS: die string2int variante hab ich grad umgesetzt...
    das dumme daran ist... ich hab aktuell 9 Kommands (voraussichtlich folgen da noch 3 4 stück) ... das heißt ich muss mir erstmal den int wert von "foo" ausgeben lassen um den dann im case abzurufen... sieht extrem dumm aus und ohne kommentare ist man da echt aufgeschmissen.... sieht dann so aus:

    Code:
    unsigned int string2int(const char* str, int h = 0)
    {
       return !str[h] ? 5381 : (string2int(str, h + 1) * 33) ^ str[h];
    }
    
    int main() {
    // abgeschnitten
    switch (string2int(command[0].c_str())) {
       case /* test */ 2087933171 :
         std::cout << "es wurde test eingegeben" << std::endl;
         break;
       case /* foo */ 193420387 :
         std::cout << "es wurde foo eingegeben" << std::endl;
         break;
       default:
         std::cout << "befehl nicht gefunden" << std::endl;
       }
    }
    
     
  14. rubricanis

    rubricanis Homo ludens

    Registriert seit:
    22 August 2011
    Beiträge:
    658
    Ort:
    Ottendorf, Niederösterreich
    Dein string2int ist wirklich unglücklich, da die Werte unverständlich sind, enums sind da weit besser. Der Vorzug von C++ ist u.A. dass du HL (Heigh Level, Klassen etc) gut mit LL mischen kanst und volle Kontrolle über das Laufzeitverhalten hast. Gerade am Anfang neigt man dazu alles HL zu machen, aber mit der Zeit wird das immer weniger da dann oft das Laufzeitverhalten schlecht ist. Wichtig ist z.B. ob du die Sachen statisch oder auf dem Stack allokieren kannst, oder new verwenden must. New und virtuelle Funktionen erfordern immer eine Redirektion was hinsichtlich Laufzeitverhalten (wg. Prozessor Catch) nicht so gut ist, Konstanten sind diesbzgl. immer gut. Und bei C++ geht es immer auch um gutes Laufzeitverhalten, sonst könnte man auch eine andere HL Sprache verwenden die produktiver ist.

    Noch etwas: Verstänlichkeit, d.h. auch treffende Namen (und nicht Macro wie oben), ist wichtig um den Überblick bei Veränderungen zu behalten, und die kommen sicherlich. Na ja, wird schon! :)
     
  15. Sadakazu

    Sadakazu Member

    Registriert seit:
    7 Juni 2016
    Beiträge:
    140
    JUCHUUU ^^ Hab das mit den Enums und Switch Case ans laufen bekommen :D

    So hab ichs jetzt mal testweise gelöst....
    Code:
    #include <iostream>
    #include <string>
    #include <sstream>
    #include <unordered_map>
    #include <exception>
    
    // Soll nur testweise rest des cmd Arrays ausgeben das der Benutzer eingegeben hat... zb start foo bar ausgabe foo bar....
    class DoSomth {
    public:
        // Soll nur testweise rest des cmd Arrays ausgeben das der Benutzer eingegeben hat... zb start foo bar ausgabe foo bar....
       void make_dummtuech(std::string args[]){
         std::cout << args[1] << " " << args[2] << std::endl;
       }
    };
    
    class CommandBuilder {
    public:
        //Ein paar beispiel "commands"
       enum Command {
         ON,
         OFF,
         RESET,
         START
       };
       // unordered map mit strings nicht chars
       typedef std::unordered_map<std::string, Command> CommandMap;
    
       //Die map mit den commands füllen
       CommandMap cmdmap = {
             {"off", Command::OFF},
             {"on", Command::ON},
             {"reset", Command::RESET},
             {"start", Command::START}
         };
    
    // der "command listener" erwartet die eingabe des benutzers....
    void listen() {
    
       std::cout  << "> ";
       std::string cmd[3];
       std::string line;
       getline(std::cin, line);
       std::istringstream stream(line);
      for (int i=0; stream.good(); i++) {
      stream >> cmd[i];
      }
      std::cin.clear();
      std::cin.sync();
       execute(cmd);
    }
    
    private:
    //Führt das Command aus 
    void execute(std::string command[]) {
       try {
           Command cmd = cmdmap.at(command[0]);
           switch (cmd) {
           //Fall through
           case Command::ON :
           case Command::START:
             std::cout << "es wurde on oder start eingegeben" << std::endl;
             DoSomth a;
             a.make_dummtuech(command);
             break;
           case Command::RESET:
             std::cout << "Es wurde reset eingegeben" << std::endl;
             break;
           case Command::OFF:
             std::cout << "Es wurde off eingegeben" << std::endl;
             break;
           default: /* wird gar nicht ausgeführt da er in eine exception läuft siehe Catch */
             std::cout << "command nicht gefunden" << std::endl;
             break;
           };
         } catch (const std::exception& e) {
            // Liefert in der regel _Map_Base::at zurück irgend wie logisch da zb foobarblub nicht in der map vorhanden ist
           std::cout << "Der Befehl wurde leider nicht Gefunden! Errorcode: " << e.what() << std::endl;
         }
         //bereinigen des input streams
         std::cin.ignore();
         
        // zurück in den listener springen
        listen();
    }
    };
    
    
    
    int main(void) {
    
       CommandBuilder cmd;
    
       cmd.listen();
    
       return 0;
    }
    Ist vielleicht immer noch nicht die sauberste lösung, aber immer noch sauberer als 10 if else if blöcke zu verwenden.....
    Da lob ich mir schon irgendwie Java mit seinen Interfaces xD
     
  16. rubricanis

    rubricanis Homo ludens

    Registriert seit:
    22 August 2011
    Beiträge:
    658
    Ort:
    Ottendorf, Niederösterreich
    Warum so kompliziert? Ich denke das kommt von dem Java-Unsinn jede Bagatelle als eine Klasse zu implementieren. Außerdem, woher weißt du dass du die Funktionalität nur in Main brauchst? Was ist z.b, mit Strings die du irgendwo anders her bekommst (TCP/IP, Konfiguration, ...) ? C++ ist eben nicht (!) Java.

    Bei mir sähe das etwa folgendermaßen aus:
    Code:
    ///// cmd.hpp /////
    
    enum class Cmd { Error,  ... };
    
    const char * str_from_cmd(Cmd cmd);
    Cmd  cmd_from_str(const char*);
    // evtl. weitere Funktionen/Klassen die mit Kommandos zusammenhängen.
    
    ///// cmd.cpp /////
    
    #include <unordered_map> // steht nicht im Header!
    #include <exception>
    
    typedef std::unordered_map<const char*, Cmd> CommandMap;
    
    static  CommandMap cmdmap = { ... }; // static!
    
    const char * str_from_cmd(Cmd cmd){....}
    Cmd  cmd_from_str(const char*) {...}
    
    Dann kannst du es in Main oder wo auch immer verwenden, z.B. auch in irgendeiner einer Klasse wenn das angezeigt ist. Natürlich kann man das auch noch anders machen, z.B. inline verwenden, die Exception auswerfen und einiges mehr. KISS: keep ist stupid simple, in C++ kann man das! :)
     
  17. Yamagi

    Yamagi Possessed With Psi Powers Mitarbeiter

    Registriert seit:
    14 April 2004
    Beiträge:
    8.747
    Ort:
    Schleswig-Holstein
    Ignoriert mich besser, aber: Ein assoziativer Container ist lediglich für simple Implementierungen geeignet. Sobald (performante) Autovervollständig von Haupt-, Unterkommando und Optionen ins Spiel kommt, landet man schnell bei den guten Rot-Schwarz-Bäumen. :)
     
  18. KobRheTilla

    KobRheTilla used register

    Registriert seit:
    20 Januar 2011
    Beiträge:
    1.065
    Wenn du den Code ordentlich formatierst, ist das if-else sogar übersichtlicher. Letztendlich gibst du den Kommandos nur eine numerische Identität, die du dann im case-Block auswertest. Was genau hast du denn davon? Du denkst viel zu kompliziert.

    Rob
     
  19. Sadakazu

    Sadakazu Member

    Registriert seit:
    7 Juni 2016
    Beiträge:
    140
    Uff...
    Ich versuchs mal so zu erklären das es Sinn ergibt...
    Vorweg... ich weiß... VIIIEEEL zu hoch gesteckt das Ziel.... jedenfalls für nen Anfänger...

    Also ich will mir ein Programm schreiben, das "dynamisch" Minecraft Server in einer Screensession startet.
    Dazu will ich im Programm unterschiedliche "Gruppen" erstellen.... Diese sollen vom Benutzer auch definiert werden können...
    Das Ganze Landet dann in einer sqlite Datenbank (später aktuell bin ich noch am grundkonstruckt)

    Das Programm soll dann mittels Kommandos die einzelnen screensessions steuern....
    z.b. stop proxy -> stopt erst den proxy server und beendet dann die screen session
    stop lobby 1 -> stoppt erst Lobby-1 und beendet dann die screen session (deswegen übrigens auch das Array mit einer größe von 3)
    start lobby 5 -> startet nach einander 5 screensessions und 5 Lobby-Server
    stop GRUPPEN_NAME 1 -> stoppt GRUPPEN_NAME-1

    Usw...
    Dazu benötige ich halt folgende Kommandos....
    start -> startet einen prozess
    stop -> stopt den prozess
    create group -> erstellt eine neue Gruppe
    modify group -> bearbeitet eine vorhandene gruppe
    delete group -> löscht die gruppe
    compile spigot -> kompiliert eine neue (andere) Minecraft server version (zb compile spigot 1.11.2)
    exit || quit || shutdown || end || stop all -> beendet alle laufenden Prozesse und beendet das programm
    install -> legt sqlite datenbank an und füllt sie mit default werten
    help -> zeigt kurze hilfe übersicht an

    Es kann natürlich auch sein das mir im laufe der zeit noch weitere Funktionen einfallen die man mittels Commands steuern soll/kann...
    Daher war die idee eines Design Patterns halt, damit ich jedes Kommando in einer eigenen cpp Datei auslagern kann....
    zb start.cpp, stop.cpp create_group.cpp (oder allgemein group.cpp?) usw usf....

    Ich weiß das das Ziel extrem hoch gesteckt ist für einen Anfänger....
    Die meisten Sachen Funktionieren aber auch schon ;) nur die Command Geschichte und sqlite fehlt noch...
    wobei sqlite als Entwurf auch schon bereit liegt....

    Später soll das ganze noch weiter ausgebaut werden (aber erst viiiieeeellllll später) als Client <-> Server Anwendung
    Sprich:
    Server Managed Proxy und verwaltet die Clients... (start stop create group "signale" an client senden)
    Clients verwalten die Gruppen sollen aber auf sqlite Inhalte von Server zugreifen können bzw Server schickt sqlite Inhalte an Client
    Warum?
    Weil man evtl sein Netzwerk ausweiten möchte und dazu einen zweiten dirtten vierten usw Dedizierten Server anmietet...
    Dort braucht man sowas wie den Proxy Server aber nicht.... Außerdem wärs kompfortabler sich nur auf einer konsole anzumelden und von dort aus das gesamte Netzwerk steuern zu können....

    Und im nächsten Schritt war die Überlegung ne QT Anwendung zu basteln die dann auf Server zugreift um von dort aus halt das ganze zu "verwalten"

    Aber..... da bin ich definitiv noch weit weit weit und noch viel weiter von weg.....
    ? Was meinst du?
    War nur ein beispiel bzw entwurf... das ganze würd ich natürlich in header und cpp auslagern und dann über die main bzw die teile des codes aufrufen, wo ich sie brauche..
    Problem war bis heute morgen halt noch, das es gar nicht erst funktioniert hat... ^^
    Das tuts jetzt aber ... muss es nur noch in meinem aktuellen Programm "implementieren".....

    Muss aber auch dazu sagen... so viel hocke ich da auch nicht vor ^^
    Ich mach halt immer mal hier ne halbe stunde da mal 10 minuten.... ;)
    Ernstgemeinte frage.... warum eigentlich immer char? bin ich so sehr php und java verwönt? MUSS ich mir das umgewöhnen in chars zu denken?
    Strings sind meiner meinung nach viel schöner :D
    Warum den header nicht in cmd.hpp, und cmd.hpp in cmd.cpp?
     
  20. Yamagi

    Yamagi Possessed With Psi Powers Mitarbeiter

    Registriert seit:
    14 April 2004
    Beiträge:
    8.747
    Ort:
    Schleswig-Holstein
    Ich hätte meine Klappe halten sollen... Ich meinte, dass man mit assoziativen Containern zwar schnell eine funktionierende Eingabezeile implementieren kann, man aber für weitergehende Funktionen wie Vervollständigung der Kommandos, selektive History und so weiter eine andere, effizienter durchsuchbare Datenstruktur benötigt. Wenn du einen assoziativen Container hast, musst du (im einfachen Fall) die Nutzereingabe mit jedem Kommando vergleichen, bis du das passende Kommando gefunden hast. Das ist sehr teuer und entsprechend langsam, wird schon bei wenigen Kommandos zum Problem. Daher nimmt man für solche Zwecke oft Suchbäume, vor allem AVL-Bäume oder eben die vom Laufzeitverhalten recht ähnlichen Rot-Schwarz-Bäume.

    Aber das sind beides keine Datenstrukturen, die man als Anfänger mal so nebenbei versteht, geschweige denn korrekt in effizienten Code gegossen bekommt. Sie dürften eher verwirren. Daher: Ignoriert mich einfach. :)
     
  21. Sadakazu

    Sadakazu Member

    Registriert seit:
    7 Juni 2016
    Beiträge:
    140
    Ok, dann danke das wir drüber gesprochen haben :D

    Hab das Commando ding von mir oben aber mal in meinem Programm mit eingepflegt... läuft super.... ^^
     
  22. rubricanis

    rubricanis Homo ludens

    Registriert seit:
    22 August 2011
    Beiträge:
    658
    Ort:
    Ottendorf, Niederösterreich
    Vorab: Die Lösungen von KobRheTilla und Yamagi sind natürlich auch möglich und möglicherweise besser, es kommt eben immer darauf an wie man die jeweilige Funktionalität ins Gesammt-Konzept einordnet. Ich selbst mache mit der Cmd-Line meist nicht viel rum und benutze If-Then-Else, meist in getrennten Funktionen für Kommandos und Optionen. Gut formatiert natürlich. :) In deinem Falle war mir vor allem wichtig dich auf Enums aufmerksam zu machen. Class Enum ist ein echter Fortschritt von C++11, unterstützt die Optimierungsmöglichkeiten des Kompilers und ermöglicht bessere Fehlermeldungen.
    Das kannst du machen wie du willst. Strings sind schöner, aber oft unnötiger Overhead. Von Strings kannst du leicht einen char* bekommen, umgekehrt koste Laufzeit. Strings sind (meist) eine vergleichsweise teuere Angelegenheit. Ich neige außerdem zu einem gewissen Minimalismus und hatte hier zunächst dein Main im Kopf.
    Sorry, mein Fehler. Ist natürlich richtig dass du cmd.hpp mit inkludieren musst. Was ich mit der Anmerkung deutlich machen wollte ist dass man in der Header-Datei nur das inkludieren sollte was man dort (!) auch braucht. Und in diesem Besispiel-Fall braucht man unordered_map und exception nur im Source-Code. Der Punkt ist dass die Kompilierzeiten und Unübersichtlichkeit dadurch minimiert werden und die Wiederverwendbarkeit in anderen Projekten verbessert wird.

    PS: Von Java und PHP ist man nicht verwöhnt, man hat nur schlechte Angewohnheiten! ;)
     
  23. Sadakazu

    Sadakazu Member

    Registriert seit:
    7 Juni 2016
    Beiträge:
    140
    Sag ich doch... verwöhnt xD (verwöhnte göhre) ^^
    Okaaayyyyy.... dann hab ich das etweder falsch verstanden gehabt oder man hats mir falsch erklärt xD
    Also mal eben alle hpp's umstricken xD