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:rogram_options;
class FooCommand : public CommandContext {
public:
FooCommand();
public:
const std::string& name() const;
private:
std::string m_name;
po: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:rogram_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:ath m_path;
po: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:arse(int argc, char **argv) {
po::store(po:arse_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
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:rogram_options;
class FooCommand : public CommandContext {
public:
FooCommand();
public:
const std::string& name() const;
private:
std::string m_name;
po: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:rogram_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:ath m_path;
po: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:arse(int argc, char **argv) {
po::store(po:arse_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