2011-01-13 56 views
5

有誰知道一個C++庫,將提供一個基於文本的交互界面?我想創建一個應用程序的兩個版本;一個基於控制檯的程序,可執行命令行上的任何操作或交互式控制檯以及基於GUI的程序(Mac Cocoa和Windows MFC)。兩個版本都將共享一個通用的C++後端。跨平臺,命令完成基於文本的交互界面

對於基於控制檯的程序,我希望readline(我不能使用,因爲這個應用程序將是封閉的源代碼)的類似歷史記錄能力與命令完成(例如Tab激活)。

或許有這樣的事情已經可用?

回答

1

更新:我還沒有找到一個滿意的(跨平臺)解決歷史/完成選項,所以我忽略了暫時的,但我想更新如何,我實現了這個問題一個簡單的互動課程。這個界面不適合主流用戶,但是我發現在實現過程中測試我的代碼非常方便。下面是實施初期,可能會幫助別人了(注意,此代碼引用的方法,我還沒有出版類型,所以將不編譯開箱)

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 

互動.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

沒有特定的順序,(我使用他們沒有,),你應該看看:

如果沒有那些認爲你有另一種可能性的人,也許它可能更受歡迎。將後端作爲守護進程寫入,並使前端成爲一個笨拙的程序,通過任何形式的進程間通信與後端進行通信。然後,您可以使用任何GPLed庫作爲您的前端,因爲您可以將前端作爲開源發佈。當然,這會暴露前端和後端之間的通信協議,所以你必須肯定會好起來的與當然,其他人可能覺得有必要進行自定義您的前端,甚至讓自己的可能性。但是,假設你的價值在後端,這不應該成爲一個特別的問題。它甚至可以被認爲是一個優點,它可以讓任何有聰明想法的人以新的和意想不到的方式使用你的軟件,只會增加軟件的受歡迎程度。

+0

非常感謝您的建議。所有這些庫僅適用於UNIX/LINUX,而不適用於不滿足跨平臺要求的Windows。目前我寫了自己的簡單命令解釋器,並忽略了歷史/命令完成方面的內容。 TBH這可能足以讓我快樂一段時間。 – trojanfoe 2011-01-15 15:40:11