2011-01-13 14 views
5

Czy ktoś wie o bibliotece C++, która zapewni interaktywny interfejs tekstowy? Chcę utworzyć dwie wersje aplikacji; program oparty na konsoli, który wykona wszelkie czynności podane w linii poleceń lub interaktywnie na konsoli, a także program oparty na GUI (Mac Cocoa i Windows MFC). Obie wersje będą miały wspólny backend C++.Platforma krzyżowa, interaktywny interfejs tekstowy z zakończeniem polecenia

Do programu opartego konsoli chciałbym podobne zdolności historii do readline (co nie można używać jako aplikacja ta zostanie zamknięte źródła) z realizacji poleceń (Tab aktywowane na przykład).

Być może coś takiego jest już dostępne?

Odpowiedz

1

Aktualizacja: Nie znalazłem zadowalającego (wieloplatformowego) rozwiązania opcji historia/zakończenie, więc na razie zignorowałem to, ale chciałem zaktualizować to pytanie z tym, jak zaimplementowałem prosta klasa interaktywna. Ten interfejs nie będzie dla głównego użytkownika, ale jestem bardzo przydatny do testowania mojego kodu podczas implementacji. Poniżej znajduje się wstępna realizacja które mogłyby pomóc innym out (zauważ, że ten kod odwołuje metod i typów I nie publikowane, więc nie będzie kompilować z pudełka)

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

W przypadkowej kolejności (użyłem żadnego z nich) należy spojrzeć na:

Jeśli żadna z Ci, którym się podoba, masz jedną inną możliwość, a być może to nawet może być preferowane. Napisz backend jako demon i niech frontenda będzie głupim programem, który komunikuje się z backendem za pośrednictwem dowolnej formy komunikacji między procesami. Następnie możesz użyć dowolnej biblioteki GPL dla twojego frontendu bez żadnego problemu, ponieważ możesz zwolnić frontend jako open source. Oczywiście będzie to narazić protokół komunikacyjny pomiędzy frontendem a backendem, więc musisz być pewien, że wszystko z nim w porządku, a także oczywiście, że inni mogą odczuwać potrzebę dostosowywania twojego interfejsu, a nawet tworzenia własnych. Ale zakładając, że twoja wartość jest w backendu, nie powinno to stanowić szczególnego problemu. I może nawet być uważany za plus, pozwoliłby każdemu, kto ma świetny pomysł, na korzystanie z oprogramowania w nowy i nieoczekiwany sposób, zwiększając jedynie popularność swojego oprogramowania.

+0

Wielkie dzięki za sugestie. Wszystkie te biblioteki są przeznaczone tylko dla systemu UNIX/LINUX, a nie dla systemu Windows, który nie spełnia wymagań dotyczących wielu platform. W tym momencie napisałem swój własny prosty interpreter poleceń i zignorowałem aspekt zakończenia historii/polecenia. TBH to prawdopodobnie wystarcza, bym był szczęśliwy przez jakiś czas. – trojanfoe