2011-01-13 8 views
5

Kennt jemand eine C++ - Bibliothek, die eine textbasierte interaktive Schnittstelle bereitstellt? Ich möchte zwei Versionen einer Anwendung erstellen. ein konsolenbasiertes Programm, das alle Aktionen ausführt, die in der Befehlszeile oder interaktiv an der Konsole ausgeführt werden, sowie ein GUI-basiertes Programm (Mac Cocoa und Windows MFC). Beide Versionen teilen sich ein gemeinsames C++ - Backend.Plattformübergreifend, Interaktive textbasierte Schnittstelle mit Befehlsabschluss

Für das konsolenbasierte Programm möchte ich ähnliche History-Fähigkeiten zu readline (die ich nicht verwenden kann, da diese Anwendung geschlossen wird Quelle) mit Befehlsabschluss (Tab-aktiviert zum Beispiel).

Vielleicht ist so etwas schon vorhanden?

Antwort

1

Update: Ich habe keine zufriedenstellende (plattformübergreifende) Lösung für die History/Completion-Option gefunden, also habe ich das vorläufig ignoriert, aber ich wollte diese Frage mit meiner Implementierung aktualisieren eine einfache interaktive Klasse. Diese Schnittstelle wird nicht für Mainstream-Benutzer sein, aber ich finde es sehr praktisch, meinen Code während der Implementierung zu testen. Unten ist die erste Implementierung, die andere aus (beachten Sie, dass dieser Code Methoden und Typen verweist Ich habe nicht veröffentlicht, so wird nicht aus dem Kasten heraus kompilieren) helfen könnten

Interact.h:

#ifndef INTERACT_H 
#define INTERACT_H 

class Interact; 
class InteractCommand; 
typedef void (Interact::*PF_FUNC)(const InteractCommand &command, const StringVector &args); 

struct InteractCommand 
{ 
    const char *command; 
    const char *argDesc; 
    const char *desc; 
    PF_FUNC func; 
}; 

class Interact 
{ 
private: 
    static Log m_log; 
    static InteractCommand m_commands[]; 
    static unsigned m_numCommands; 

    bool m_stop; 
    Database &m_database; 
    StringVector &m_dirs; 

public: 
    Interact(Database &database, StringVector &dirs); 
    ~Interact(); 

    /** 
    * Main 'interact' loop. 
    * 
    * @return true if the loop exitted normally, else false if an error occurred. 
    */ 
    bool interact(); 

private: 
    // Functions 
#define DEFFUNC(f) void FUNC_##f(const InteractCommand &command, const StringVector &args) 
    DEFFUNC(database); 
    DEFFUNC(dirs); 
    DEFFUNC(exit); 
    DEFFUNC(help); 
#undef DEFFUNC 


    /** 
    * Print usage information for the specified command. 
    * 
    * @param command The command to print usage for. 
    */ 
    static void usage(const InteractCommand &command); 

    static void describeCommand(string &dest, const InteractCommand &command); 
}; 

#endif // INTERACT_H 

Interact .cpp:

#include "Interact.h" 

Log Interact::m_log("Interact"); 

#define IFUNC(f) &Interact::FUNC_##f 
InteractCommand Interact::m_commands[] = 
{ 
    { "database", "<file>|close", "Use database <file> or close opened database", IFUNC(database) }, 
    { "dirs", "dir[,dir...]", "Set the directories to scan", IFUNC(dirs) }, 
    { "exit", 0, "Exit", IFUNC(exit) }, 
    { "help", 0, "Print help", IFUNC(help) } 
}; 
#undef IFUNC 

unsigned Interact::m_numCommands = sizeof(m_commands)/sizeof(m_commands[0]); 

Interact::Interact(MusicDatabase &database, StringVector &dirs) : 
    m_stop(false), 
    m_database(database), 
    m_dirs(dirs) 
{ 
} 

Interact::~Interact() 
{ 
} 

bool Interact::interact() 
{ 
    string line; 
    StringVector args; 
    unsigned i; 

    m_stop = false; 
    while (!m_stop) 
    { 
     args.clear(); 
     cout << "> "; 
     if (!getline(cin, line) || cin.eof()) 
      break; 
     else if (cin.fail()) 
      return false; 

     if (!Util::splitString(line, " ", args) || args.size() == 0) 
      continue; 

     for (i = 0; i < m_numCommands; i++) 
      if (strncasecmp(args[0].c_str(), m_commands[i].command, args[0].length()) == 0) 
       break; 
     if (i < m_numCommands) 
      (this->*m_commands[i].func)(m_commands[i], args); 
     else 
      cout << "Unknown command '" << args[0] << "'" << endl; 
    } 
    return true; 
} 

void Interact::FUNC_database(const InteractCommand &command, const StringVector &args) 
{ 
    if (args.size() != 2) 
    { 
     usage(command); 
     return; 
    } 

    if (args[1] == "close") 
    { 
     if (m_database.opened()) 
      m_database.close(); 
     else 
      cout << "Database is not open" << endl; 
    } 
    else 
    { 
     if (!m_database.open(args[1])) 
     { 
      cout << "Failed to open database" << endl; 
     } 
    } 
} 

void Interact::FUNC_dirs(const InteractCommand &command, const StringVector &args) 
{ 
    if (args.size() == 1) 
    { 
     usage(command); 
     return; 
    } 
    // TODO 

} 

void Interact::FUNC_exit(const InteractCommand &command, const StringVector &args) 
{ 
    m_stop = true; 
} 

void Interact::FUNC_help(const InteractCommand &command, const StringVector &/*args*/) 
{ 
    string descr; 
    for (unsigned i = 0; i < m_numCommands; i++) 
    { 
     describeCommand(descr, m_commands[i]); 
     cout << descr << endl; 
    } 
} 

void Interact::usage(const InteractCommand &command) 
{ 
    string descr; 
    describeCommand(descr, command); 
    cout << "usage: " << endl; 
    cout << descr << endl; 
} 

void Interact::describeCommand(string &dest, const InteractCommand &command) 
{ 
    dest.clear(); 
    string cmdStr = command.command; 
    if (command.argDesc != 0) 
    { 
     cmdStr += " "; 
     cmdStr += command.argDesc; 
    } 
    Util::format(dest, " %-30s%s", cmdStr.c_str(), command.desc); 
} 
2

In keiner bestimmten Reihenfolge, (ich habe keine von ihnen verwendet,) Sie einen Blick auf haben sollte:

Wenn keine diese haben deine Vorliebe, du hast noch eine andere Möglichkeit und vielleicht wird sie sogar bevorzugt. Schreiben Sie Ihr Backend als einen Daemon und lassen Sie das Frontend ein dummes Programm sein, das mit dem Backend über irgendeine Form der Interprozesskommunikation kommuniziert. Sie können dann jede GPLed-Bibliothek für Ihr Frontend ohne Probleme verwenden, da Sie das Frontend als Open Source freigeben können. Natürlich wird dies das Kommunikationsprotokoll zwischen Frontend und Backend offen legen, also müssen Sie sicher sein, damit einverstanden zu sein und natürlich die Möglichkeit, dass andere das Bedürfnis haben, Anpassungen an Ihrem Frontend vorzunehmen und vielleicht sogar ihre eigenen zu machen. Aber vorausgesetzt, dass Ihr Wert im Backend ist, sollte dies kein besonderes Problem darstellen. Und es kann sogar als ein Plus betrachtet werden, es würde jedem mit einer guten Idee erlauben, Ihre Software auf neue und unerwartete Weise zu verwenden, was nur die Popularität Ihrer Software erhöht.

+0

Vielen Dank für die Vorschläge. Alle diese Bibliotheken sind nur für UNIX/LINUX und nicht für Windows, das die plattformübergreifende Anforderung nicht erfüllt. Im Moment habe ich meinen eigenen einfachen Befehlsinterpreter geschrieben und habe den History/Command Completion-Aspekt ignoriert. TBH das wird wahrscheinlich genug sein, um mich für eine Weile glücklich zu machen. – trojanfoe