Re: console programm with alias design
Olaf wrote:
Hi,
I try to design a program which has to run on console. There is only one
exe/binary and depend on the calling name argv[0] the different
tasks/commands should be performed as aliases. It's the technique by
busybox or linux lvm tools in C and avoids a bunch of different
binaries. Now I want to write those using C++ classes and boost.
You mean something like the following?
#include <string>
#include <map>
#include <iostream>
#include <ostream>
typedef int (*main_fct)( int, char** );
typedef std::map< std::string, main_fct > registry;
int main_a ( int argn, char ** args ) {
std::cout << "a\n";
}
int main_b ( int argn, char ** args ) {
std::cout << "b\n";
}
int main ( int argn, char ** args ) {
registry main_table;
main_table[ "a" ] = &main_a;
main_table[ "b" ] = &main_b;
registry::iterator main_iter = main_table.find( args[0] );
if ( main_iter == main_table.end() ) {
std::cerr << "invocation by unknown name\n";
exit(-1);
}
return ( main_iter->second( argn, args ) );
}
Ok, now I'm in design considerations. I have problems with the API for
this purpose.
This class starts as base class of all commands to be implemented:
---8<---
class CommandContext : public boost::noncopyable {
public:
virtual const std::string& name() const = 0;
};
--->8----
This is a sample command:
---8<---
namespace po = boost::program_options;
class FooCommand : public CommandContext {
public:
FooCommand();
public:
const std::string& name() const;
private:
std::string m_name;
po::options_description m_desc;
po::variables_map m_vm;
};
FooCommand::FooCommand()
: m_name("foo"),
m_desc("foo description")
{
m_desc.add_options()
("include-path,I",
po::value< vector<string> >()->composing(),
"include path")
;
}
const std::string& FooCommand::name() const {
return m_name;
}
--->8---
Similar another command's implementation:
---8<---
BarCommand::BarCommand()
: m_name("bar"),
m_desc("bar description")
{
m_desc.add_options()
("compression", po::value<int>(), "set compression level")
;
}
--->8---
and the app class self with main():
---8<---
namespace po = boost::program_options;
namespace fs = boost::filesystem;
class app : public boost::noncopyable {
public:
app(int argc, char **argv);
int exec();
private:
void setupCmdOptions();
void parse(int argc, char **argv);
private:
fs::path m_path;
po::options_description m_desc;
po::variables_map m_vm;
bool m_start;
bool m_alias;
};
int main(int argc, char **argv) {
try {
app a(argc, argv);
return a.exec();
}
catch(std::exception& e) {
cerr << e.what();
return 1;
}
catch(...) {
cerr << "unexpected exception";
return 1;
}
}
app::app(int argc, char **argv)
: m_path(argv[0], fs::native),
m_desc("Allowed options"),
m_start( true ),
m_alias( false )
{
setupCmdOptions();
parse(argc, argv);
}
/// Declare the supported options.
void app::setupCmdOptions() {
m_desc.add_options()
("help", "produce help message")
("version,v", "print version string")
;
}
void app::parse(int argc, char **argv) {
po::store(po::parse_command_line(argc, argv, m_desc), m_vm);
po::notify(m_vm);
if (m_vm.count("help")) {
cout << "This is app v0.8.15.\n\n"
<< "Usage: " << m_path.leaf() << " [options]\n\n";
cout << m_desc << "\n";
m_start = false;
return;
}
if (m_vm.count("version")) {
cout << "app v0.8.15\n";
}
}
int app::exec() {
if ( m_start )
cout << "Start app\n";
return 0;
}
The command classes should register self at the app class, using the
specific command line options (added to app's options_description /
variables_map. Therefore an "app help" lists the common help message,
"app foo help" the help for foo command only.
I suffer from the design of all the classes APIs. Using boost isn't the
problem yet :-)
Any ideas, short samples, help here?
I have a very hard time relating the code you posted to the first paragraph.
Why does it need to be so complicated? I suspect there are other problems,
unrelated to the args[0] switch, that you are trying to solve here.
Best
Kai-Uwe Bux