Building a commandline interface with boost::program_options
My ONTF project “DomBackUp” can be configured and run via the command-line without having the configuration database installed on the Domino server.
DomBackUp is a fork of nKBackup by Tom Lyne et al. I found the command-line interface code a bit hard to maintain when it comes to enhancements.
So I was looking for an alternative way to create the command-line interface in C++ platform-independent.
boost::program_options is a library that makes it easy to parse command-line options, for example, for console applications.
Here is the code snippet from the DomBackUp project
try {
#include<boost/program_options/options_description.hpp>
#include<boost/program_options/parsers.hpp>
#include<boost/program_options/variables_map.hpp>
...
namespace po = boost::program_options;
...
po::options_description description("DomBackUp usage",PO_LINE_LENGTH);
description.add_options()
("help,h", "Display this help message")
("source,s", po::value( &szSource ), "source file/folder")
("dest,d", po::value( &szDestination ), "destination file/folder")
("include-sub-dirs,inc", po::bool_switch( &bIncludeSubDirs )->default_value(false),
"Include subdirectories, applies to folder backup only (optional, default = false)")
("throttle,t", po::bool_switch( &bThrottle )->default_value(false),
"Wait short time between file writes (optional, default = false)")
("zip,z", po::bool_switch( &bZipAfterBackup )->default_value(false),
"Move file to .zip archive after backup (optional, default = false)")
("unique-filename,u", po::bool_switch( &bUniqueFileName )->default_value(false),
"appends a unix timestamp to the archive filename (optional, default = false)")
("application-type,a", po::value( &szAppType )->default_value("nsf"),
"nsf,ntf,both = application type (optional, default = nsf only)")
("input-file,f", po::value( &szInputFile ),
"Backup all the files specified in an .ind file created in the Domino data folder to <dest>")
("version,v", "Display the version number");
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).options(description).run(), vm);
po::notify(vm);
if(vm.count("help")) {
std::cout << description << "\n";
return FALSE;
}
if(vm.count("version")) {
AddInLogMessageText("V %s on %s", NOERROR, szVersionNumber.c_str(), szServer);
AddInLogMessageText("Termination complete.\n", NOERROR);
return FALSE;
}
}
catch(po::error& e) {
AddInLogMessageText((char*)e.what(), NOERROR);
return (ERR(error));
}
First of all, you have to include some header files and (optional) set the namespace to be used. The only downside of using boost::program_options is the fact that you have to build the static .lib. While most parts of the boost libraries are header only, boost::program_options is not. But once you have the library at hand, it is easy to write the code.
The first line of code creates your interface and sets the Title as well as the line-length. The line-length is 80 by default.
po::options_description description("DomBackUp usage",PO_LINE_LENGTH);
The next thing to do is to add all the options you like to control via the command-line
description.add_options()
("help,h", "Display this help message")
("source,s", po::value( &szSource ), "source file/folder")
Each option can have a description and the command, both verbose (–help) and abbreviated (-h).
In addition, you can bind the command to a variable. The variable can be of any type, string, int or boolean.
For boolean variables, you can implement some kind of toggle mechanism.
description.add_options()
("zip,z", po::bool_switch( &bZipAfterBackup )->default_value(false),
"Move file to .zip archive after backup (optional, default = false)")
In this example, &bZipAfterBackup by default is set to false. To enable the feature, simply add –zip or -z to the command-line. This will toggle from false tu true.
The –help (-h) command does not have any value binding. So we have to add a parser and some ther lines of code that are processed, when the –help (-h) command is being used.
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).options(description).run(), vm);
po::notify(vm);
if(vm.count("help")) {
std::cout << "\n" << description << "\n";
return FALSE;
}
This is what you get when you type “load nbackup -h”
Cool, isn’t it?. Just a couple of lines of code and you get a professional command-line interface.
As said before, the downside is the overhead of having the boost libraries included in your project.
But the footprint is minimal, although the boost libraries are many GB of code.