I was recently worked on a utility and wanted to add support for parsing commandline arguments, but didn’t want to reinvent the wheel and that I’ve found boost::program_options.

Requirements

Let’s say we are working on video encoder application (venc) and we want venc to be configurable via command line arguments this way

venc [--encoder h264|vp8, =h264][--verbose][--log PATH][--output,o PATH][--help] [--input]INPUT

where optional --encoder h264|vp8, =h264 option set used video encoder to h264 or vp8 with h264 as default. Optional --verbose option produce extended logging output, optional --log PATH save logging output to specified file PATH. Mandatory --output PATH option (or -o) save encoded input video PATH and positional INPUT (with optional --input) defines encoded video file name.

We also want from the library to generate nicely formated help message in case --help argument is used, this way

./venc --help
Usage: venc [OPTION]... -o OUTPUT [--input]INPUT

Options:

  --help                 produce help messages
  --encoder arg (=h264)  used video encoder, available encoders are h264, h265,
                         vp8 or vp9
  --verbose              verbose output
  --log arg              create log file
  -o [ --output ] arg    save converted video as arg

The Code

Sample source code is available in source/venc directory.

tip: just run scons command to build venc with SCons or refer to scons starter tutorial in case of any troubles

First we include boost/program_options.hpp header file and define po namespace and some using so the code will not become messy, this way

#include <string>
#include <iostream>
#include <boost/program_options.hpp>

using std::cout, std::endl, std::string, std::exception;
namespace po = boost::program_options;

then inside main function create instance of options_description to describe our command line options

string opt_encoder;

po::options_description opts;
opts.add_options()
   ("help", "produce help messages")
   ("encoder", po::value<string>(&opt_encoder)->default_value("h264"),  // option with default value
      "used video encoder, available encoders are h264, h265, vp8 or vp9")
   ("verbose", po::bool_switch()->default_value(false), "verbose output")  // switch option
   ("log", po::value<string>(), "create log file")  // string value option
   ("output,o", po::value<string>()->required(), "save converted video as arg");  // required option

// ...

We want --input option to be positional one so instead

venc -o encoded.mkv --input foo.mp4

we can simply write

venc -o encoded.mkv foo.mp4

to run venc. We also want --input option to be mandatory and not shown to the user in a list of options in case of --help argument used. We need one set of options for parsing (cmd_opts) and one set of options for generating help message (visible_opts).

// options not shown to the user
po::options_description hidden_opts;
hidden_opts.add_options()
   ("input", po::value<string>()->required(), "input video file");

po::positional_options_description pos_opts;  // positional options
   pos_opts.add("input", 1);

po::options_description visible_opts{"Options"};  // to generate help message
   visible_opts.add(opts);

po::options_description cmd_opts;  // parsed options
   cmd_opts.add(opts).add(hidden_opts);

The next step is to parse command line arguments array defined by argc and argv variables from main function

// parse arguments
po::variables_map args;
po::store(po::command_line_parser{argc, argv}
   .options(cmd_opts)
   .positional(pos_opts)
   .run(), args);

all command line arguments are now available in std::map like variable args so options can be accessed with [] operator (like args["output"] or args["log"], …).

Generate notifications in case any mandatory argument is missing or --help argument used with

try {
   po::notify(args);
}
catch (exception & e)
{
   if (!args.count("help"))
      cout << "error: " << e.what() << "\n\n";

   cout << "Usage: venc [OPTION]... -o OUTPUT [--input]INPUT\n"
      << "\n"
      << visible_opts << "\n";

   return 1;
}

code.

The rest of the sample just print argument values this way

cout << "encoder=" << opt_encoder << "\n"
   << "log=" << (args.count("log") ? args["log"].as<string>() : "not used") << "\n"
   << "verbose=" << std::boolalpha << args["verbose"].as<bool>() << "\n"
   << "output=" << args["output"].as<string>() << "\n"
   << "input=" << args["input"].as<string>() << "\n";

and that is all.

note: source code available there

Final Testing

To build venc you need to install program_options library from boost with

sudo apt install libboost-program-options-dev

command and then run SCons with

scons

command from directory with SConstruct file.

Running venc with output and input arguments produce

$ ./venc -o encoded.mkv foo.mp4
encoder=h264
log=not used
verbose=false
output=encoded.mkv
input=foo.mp4
done!

output. Running venc without mandatory output argument

$ ./venc foo.mp4
error: the option '--output' is required but missing

cause missing output argument error. Running venc with verbose and encoder arguments produce

$ ./venc -o encoded.mkv foo.mp4 --verbose --encoder vp8
encoder=vp8
log=not used
verbose=true
output=encoded.mkv
input=foo.mp4
done!

output.