Кроссплатформенный, интерактивный текстовый интерфейс с завершением команды - PullRequest
5 голосов
/ 13 января 2011

Кто-нибудь знает библиотеку C ++, которая будет предоставлять текстовый интерактивный интерфейс?Я хочу создать две версии приложения;консольная программа, которая будет выполнять любые действия, заданные в командной строке или в интерактивном режиме на консоли, а также программу на основе графического интерфейса (Mac Cocoa и Windows MFC).Обе версии будут иметь общий бэкэнд C ++.

Для консольной программы я хотел бы иметь схожие истории возможности с readline (который я не могу использовать, так как это приложение будет закрытым исходным кодом) с завершением команды(Например, активируется с помощью вкладок).

Возможно, что-то подобное уже доступно?

Ответы [ 2 ]

2 голосов
/ 15 января 2011

Ни в каком конкретном порядке, (я не использовал ни одного из них), вы должны взглянуть на:

Если ни один из них вам не по вкусу, у вас есть еще одна возможность, и, возможно, она может даже оказаться предпочтительной.Напишите свой бэкэнд в качестве демона, и пусть он будет глупой программой, которая взаимодействует с бэкэндом через любую форму межпроцессного взаимодействия.Затем вы можете без каких-либо проблем использовать любую GPL-библиотеку для своего веб-интерфейса, поскольку вы можете использовать его как открытый исходный код.Конечно, это обнажит протокол связи между внешним и внутренним интерфейсом, поэтому вы должны быть уверены, что с этим все будет в порядке, и, конечно, вероятность того, что другие могут почувствовать необходимость внести изменения в ваш внешний интерфейс и, возможно, даже сделать свои собственные.Но если предположить, что ваше значение в любом случае находится в бэкэнде, это не должно создавать особых проблем.И это даже можно считать плюсом, это позволит любому, у кого есть блестящая идея, использовать ваше программное обеспечение новыми и неожиданными способами, только увеличивая популярность вашего программного обеспечения.

1 голос
/ 15 января 2011

Обновление: я не нашел удовлетворительного (кроссплатформенного) решения для опции истории / завершения, поэтому я пока игнорировал это, но я хотел обновить этот вопрос с помощью того, как я реализовалпростой интерактивный класс.Этот интерфейс не предназначен для основного пользователя, но я считаю его очень удобным для тестирования моего кода во время реализации.Ниже приведена первоначальная реализация, которая может помочь другим (обратите внимание, что этот код ссылается на методы и типы, которые я не публиковал, поэтому не будет компилироваться сразу)

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);
}
...