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