A traditional library for command line processing is the getopt library. For C++ programmers, a powerful alternative is the Boost::Program_options library (PO). PO allows you to specify your options in a rather simple and flexible way:
po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") ("compression", po::value<int>(), "set compression level") ;
What I like about OI is that it does all the heavy lifting for you, like putting the command-line options into variables:
desc.add_options() ("ouput-file,o", po::value<std::string>(&arg_output_file)->default_value("out.txt"), "Save output to file.") ;
Now you can access your program and pass command line options to it:
foo foo --output-file=foo.out foo -o foo.out
And after the application processes your command line options, the values of output-file will be found in the
arg_output_file variable. Now you just have to create a bunch of
arg_ variables, and then pass them to
add_options. However, this quickly becomes hard to maintain when you realize that to add a new command line options, you may have to edit multiple files to get it to work right. This is where the awesome power of X-Macros come into play.
X-Macros are undefined macros that are contained in a file, say
//XCMD(lname, sname, desc, type, def) XCMD(help, h, "display help message", bool, false) XCMD(version, v, "display version information", bool, false)
One uses these X-Macros by defining
XCMD in a source file, including
app.cmds, and then undefining
XCMD. By defining XCMD different ways, you can reuse your cmds file.
#define XCMD(lname, sname, desc, type, def) type arg_##lname ; #include "app.cmds" #undef XCMD po::options_description desc("Allowed options"); desc.add_options() #define XCMD(lname, sname, desc, type, def) ( \ #lname "," #sname, \ po::value< type >(&arg_##lname)->default_value(def), \ desc ) #include "app.cmds" #undef XCMD ;
This code would become the following after preprocessing the X-Macros.
bool arg_help; bool arg_version; po::options_description desc("Allowed options"); desc.add_options() ("version,v", po::value< bool >(&arg_version)->default_value(false) , "display help message") ("help,h", po::value< bool >(&arg_help)->default_value(false) , "display version information") ;
Now to update your command line options, all you need to do is edit your commands line file, and PO and X-Macros will do the rest for you. Isn’t that totally cool!
I’ve uploaded a simple application framework that makes use of these concepts. I’ve used it in my recent application development, including the 1.2 version of Ngila, and three other unreleased projects. Click here to download the framework. You will of course need to install Boost to use it.
The X-Macros in this framework are a bit more complicated than the examples here because they handle conditions not discussed here. These include an optional sname argument, and the ability to handle compound command line options, e.g.
arg_output_file being specified by
--output-file instead of
--output_file. I use parts of the Boost::Preprocessor library to support these things. The framework also uses additional features of Boost::Program_options, including passing arguments via a file and using positional arguments.
Try it out and let me know what you think.