2011-01-13 5 views
5

Qualcuno sa di una libreria C++ che fornirà un'interfaccia interattiva basata su testo? Voglio creare due versioni di un'applicazione; un programma basato su console che eseguirà tutte le azioni fornite sulla linea di comando o in modo interattivo alla console e un programma basato su GUI (Mac Cocoa e Windows MFC). Entrambe le versioni condivideranno un backend C++ comune.Cross platform, Interfaccia interattiva basata su testo con completamento del comando

Per il programma basato su console, mi piacerebbe che le abilità di cronologia simile a readline (che non posso utilizzare come questa applicazione sarà closed source) con il completamento del comando (scheda attivata per esempio).

Forse è già disponibile qualcosa di simile?

risposta

1

Aggiornamento: Non ho trovato una soluzione soddisfacente (multipiattaforma) per l'opzione di cronologia/completamento, quindi per il momento l'ho ignorato, ma volevo aggiornare questa domanda con il modo in cui ho implementato una semplice classe interattiva. Questa interfaccia non sarà per gli utenti mainstream, ma trovo molto utile per testare il mio codice durante l'implementazione. Di seguito è riportato l'implementazione iniziale che potrebbe aiutare gli altri (si noti che questo codice fa riferimento a metodi e tipi non ho pubblicato, quindi non compilerà out of the box)

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 nessun ordine particolare, (ho nessuno di loro usato,) si dovrebbe avere uno sguardo a:

Se nessuno dei quelli prendono la tua fantasia hai un'altra possibilità, e forse potrebbe anche essere preferito. Scrivi il tuo backend come demone e fai in modo che il frontend sia un programma stupido che comunica con il back-end tramite qualsiasi forma di comunicazione tra processi. È quindi possibile utilizzare qualsiasi libreria GPLed per il frontend senza alcun problema in quanto è possibile rilasciare il frontend come open source. Ovviamente questo esporrà il protocollo di comunicazione tra frontend e backend, quindi devi essere sicuro di essere d'accordo con questo e ovviamente la possibilità che altri possano sentire la necessità di fare personalizzazioni al tuo frontend e magari persino crearne di propri. Ma supponendo che il tuo valore sia comunque nel back-end, questo non dovrebbe rappresentare un problema particolare. E potrebbe anche essere considerato un vantaggio, permetterebbe a chiunque abbia un'idea brillante di utilizzare il software in modi nuovi e inaspettati, aumentando solo la popolarità del software.

+0

Molte grazie per i suggerimenti. Tutte queste librerie sono solo per UNIX/LINUX e non per Windows, che non soddisfa i requisiti multipiattaforma. Al momento ho scritto il mio semplice interprete di comandi e ho ignorato l'aspetto di completamento della storia/comando. Questo sarà probabilmente sufficiente per farmi felice per un po '. – trojanfoe