Parsen...
-
Ich werkel grade an einer kleinen Konsole rum und überlege, wie man eingegebene Befehle am Besten parsen kann. Testweise lasse ich Argumente gerade außer Acht und habe meinen Befehls-Parser recht einfach aufgebaut, vereinfacht:
char* input=GLOBAL.get(0); // Holt erses Argument aus Tabelle int i=0; while(input[i]) { if(input[i]>='a' && input[i]<='z') { // Kleinbuchstaben -> Großbuchstaben input[i] = (input[i] - 32); } i++; } if (util::cmpstrn(input,"EXIT")) exit_flag=1; else if (util::cmpstrn(input,"ECHO")) cout << GLOBAL.getstrn(1) << "\n\n"; else if (util::cmpstrn(input,"ABOUT")) { cout << "\n Console\nVersion 0.1 Alpha" << " (C) 2005. by BloodLord. All rights reserved."; } ...Jetzt finde ich es aber nicht sehr schön, eine endlos lange if/else if Liste zu machen die durchlaufen wird und bin auf der Suche nach einer besseren Möglichkeit, meine sinnlosen Einfälle sind dann Dinge wie:
char **commands; ... // In commands kommt eine liste mit alle befehlen... int lalala(const char* input) { // ...denen alle eine nummer zugeordnet ist... for(int i=0; i<irgendwas; i++) { if(util::cmpstrn(input) return i; } return -1; } void hmhmhm() { int temp=lalala(GLOBAL.get(0)); switch (temp) // ...welche dann aber wiederum umständlich einer Anweisung zugeordnet wird { case -1: break; case 0: exit_flag=1; break; case 1: cout << GLOBAL.getstrn(1) << "\n\n"; break; ... } }Kann man sowas nicht etwas eleganter und funktioneller realisieren?
-
Du könntest eine std::map mit Strings für deine Befehle und Funktionszeiger machen. Jede Funktion kriegt dann ungefähr wie main die Argumente übergeben? Nur so ne Idee...

-
Ich hab das mit dem MVC-Modell implementiert. Dazu hab ich ein Singleton Console und eine Basisklasse IConsoleCommand, die so aussieht:
/// Interface für Konsolenkommandos class IConsoleCommand { public: /// Gibt den Namen des Kommandos zurück virtual std::vector<std::string> getName () const = 0; /// Führt die Aktion aus virtual std::string action () = 0; /// Zeigt eine Beschreibung des Kommandos an, auch über --help virtual std::string description () const = 0; };Console sieht so aus:
class Console : public boost::noncopyable { public: typedef IConsoleCommand* ConsoleCommand; typedef std::multimap<std::string, ConsoleCommand> CommandMap; static Console* instance () { return &msInstance; } void registerCommand (ConsoleCommand); const CommandMap& getCommands () { return mCommands; } /// Startet die Konsole void run (); protected: /// Konstruktor Console () { mRunning = false; } /// virtueller Destruktor virtual ~Console () { } private: /// Liste der bereits registrierten Konsolen-Kommandos CommandMap mCommands; /// Läuft die Konsole ? bool mRunning; /// Singleton Instanz static Console msInstance; };Und Console.cpp sieht so aus:
#include "Console.hpp" #include <iostream> #include <string> #include <vector> #include <utility> using namespace Universum::UserInterface::Console; using namespace std; Console Console::msInstance; void Console::registerCommand (ConsoleCommand newCommand) { vector<std::string> names = newCommand->getName (); for (vector<std::string>::iterator i = names.begin (); i != names.end (); ++i) mCommands.insert (make_pair ((*i), newCommand)); } void Console::run () { std::string cmd = ""; CommandMap::iterator i; ConsoleCommand cmd_c; mRunning = true; while (mRunning) { cout << "$ "; getline (cin, cmd); if (cmd == "exit") mRunning = false; else if (!parse (cmd)) cout << "Kommando " << cmd << " nicht gefunden" << endl; } }Jetzt müsstest du nur noch Console::parse () und ein Kommando implementieren z.B.:
class CMDCount : public IConsoleCommand { public: CMDCount () { mCounter = 0; } virtual std::vector<std::string> getName () const { return myNames("count cnt"); } virtual std::string action () { std::string str; std::ostringstream os (str); os << "Anzahl der Aufrufe:\t" << ++mCounter << std::ends; return os.str (); } virtual std::string description () const { return "Zaehlt die Aufrufe seiner selbst"; } private: unsigned int mCounter; };... und das Kommando im Hauptprogramm registrieren:
Console& c = *Console::instance (); c.registerCommand (new CMDCount);Ist bestimmt auch nicht der effizienteste Weg, aber nicht so C-style wie ein Switch.
Für die parse Funktion würde ich Spirit benutzen, is aber recht kompliziert (wie ich finde).