console programm with alias design

O

Olaf

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.

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::eek:ptions_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::eek:ptions_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?

Thanks
Olaf
 
K

Kai-Uwe Bux

Olaf said:
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::eek:ptions_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::eek:ptions_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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,961
Messages
2,570,130
Members
46,689
Latest member
liammiller

Latest Threads

Top