How to use Boost.Program_options

If it so happens that you’re writing a console application, then chances are you will (or should) want to pass it some parameters at the command line. By default C++ has only the argc and argv parameters to the main function to facilitate this and I can assure you that working with them directly is less than fun. If you have worked with any number of command line programs you would be well aware of a common form that they take and that users expect. To get this expected behavior you should use another favourite boost library of mine: Program options.

Quite simply, Boost.Program_options is the way to do industry standard command line processing in C++, if you’re trying to implement command line arguments in your application then you should be using this library. This tutorial will take you through how to get set up and productive using boost program options with a minimum of fuss.

Obtaining, compiling and linking to boost is not covered.

EDIT: Aug 16 2014
Spurred on by a comment on this post I have written a library that extends Boost.Program_options, providing support for subcommand syntax (think SVN or Mercurial) and including convenient formatting utilities for posix style help output. It's free, open source and awesome! You can check it out here.

Application Template

So you have an program that you want to be able to accept command line parameters? Then replace (or merge) the contents of your main.cpp with the code below. This code forms the foundation of the rest of the tutorial and is boiler plate that you will find yourself writing every time you create a new command line application.

Note: the rest of the examples reference the names of objects in the code below.


#include "boost/program_options.hpp"

#include <iostream>
#include <string>

namespace
{
  const size_t ERROR_IN_COMMAND_LINE = 1;
  const size_t SUCCESS = 0;
  const size_t ERROR_UNHANDLED_EXCEPTION = 2;

} // namespace

int main(int argc, char** argv)
{
  try
  {
    /** Define and parse the program options
     */
    namespace po = boost::program_options;
    po::options_description desc("Options");
    desc.add_options()
      ("help", "Print help messages")
      ("add", "additional options")
      ("like", "this");

    po::variables_map vm;
    try
    {
      po::store(po::parse_command_line(argc, argv, desc), 
                vm); // can throw

      /** --help option
       */
      if ( vm.count("help")  )
      {
        std::cout << "Basic Command Line Parameter App" << std::endl
                  << desc << std::endl;
        return SUCCESS;
      }

      po::notify(vm); // throws on error, so do after help in case
                      // there are any problems
    }
    catch(po::error& e)
    {
      std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
      std::cerr << desc << std::endl;
      return ERROR_IN_COMMAND_LINE;
    }

    // application code here //

  }
  catch(std::exception& e)
  {
    std::cerr << "Unhandled Exception reached the top of main: "
              << e.what() << ", application will now exit" << std::endl;
    return ERROR_UNHANDLED_EXCEPTION;

  }

  return SUCCESS;

} // main

Basic option configuration

All of the below options should be added as additional lines in the
desc.add_options()
call in the application template.

Add the most simple of options

--option


("option", "Info message about option")

Add a shorthand for an option

--option or -o


("option,o", "Info message about option") // can use -o

Add an option with shorthand only

-o


(",o", "Info message about option") // must use -o

NOTE: To access a shorthand only value in a variables_map format the name as you would on the command line e.g. -o. See the Accessing Option Values section for more details.

Add an option that has an associated value

--option <value>


("option", po::value<arg_type>(), "Info message about option")

Specify that an option is required

The call to
po::notify(vm)
will throw if the option is not specified.

("option", po::value<arg_type>()->required(), "Info message")

Specify an option that can be specified multiple times

--option <value1> --option <value2> --option <value3>


("option", po::value<std::vector<arg_type> >(), "a list of values")

Accessing option values

Have the option set directly into an existing variable

I find this to be the most convenient method to use


("option", po::value<arg_type>(&existingVariable), "info message")

Check if an option was passed in the command line


if ( vm.count("option") )

To extract the value for an option manually


vm["option"].as<arg_type>()

Retrieving shorthand only options

Access to shorthand options is done exactly the same way as described above but the "option" string has to be formatted in a particular way. If there is only a shorthand option then the name for that option is considered to be "-X" where X is the letter being used as the short hand. Another way to remember this is to specify the name as it would appear on the command line.


(",t", "Shorthand only option -t")
if ( vm.count("-t") )
{
  vm["-t"].as<arg_type>()
}

Specifying Positional Options

A positional option is a value given on the command line that is not preceded by an option switch like -o or --option. An example is the following:

user@computer:$ g++ -o main main.cpp

In this case main.cpp is the positional option, and as you can see it is not associated with any option explicitly specified. Instead of being identified by an option switch (like --option) they are identified by the lack of one. When multiple positional options are available the option the value is assigned to is designated by the order in which the values appear in the command line.

user@computer:$ hg help commit -v

In this case 'help' is the first positional option and 'commit' is the second. The command will print help info for the commit subcommand. Now if the order were to be reversed:

user@computer:$ hg commit help -v

The position of the options is now reversed and the command will now try to 'commit' the location 'help'. Again the order in which values are specified determines which positional option the value is assigned to.

Example code for positional options

To specify these with boost program options add a
po::positional_options_description
object and specify the names of the options that you want to parse as positional. Add the following code block directly below the last option in the
desc.add_options()
call:

po::positional_options_description positionalOptions;
positionalOptions.add("option_name_1", <num_occurrences>);
positionalOptions.add("option_name_2", <num_occurrences>);

The option name specified as the first parameter must correspond to the name of an option already specified in the regular options description. The second parameter specifies the number of values to be associated with the option, in the simple case you will always set this to 1. The positional order of options is implied by the order in which they are added to the description, in this case 'option_name_1' is first and 'option_name_2' is second.

Next you need to make sure that the positional options are parsed along with the others and stored in the variable map. Change the
po::store()
call to the following:

po::store(po::command_line_parser(argc, argv).options(desc)
            .positional(positionalOptions).run(),
          vm);

Putting it all together

With just the information above you can create quite sophisticated command line applications, but sometimes knowing the parts does not give a good enough picture of what can be achieved. Check out below for a full example of the options implemented in a demo application. This should help to give you an idea of how things look when it’s all put together. Be aware that I have implemented some custom parsing for printing the available options in posix style which I include as part of the source package for the example application and an example of that usage output is shown immediately below. The information is parsed generically from the specified options and this prevents you from having to manually update help text when you update options (which is pretty awesome).

Best of luck, and please let me know if you have any issues.

Please note you will need version 1.50.0 of boost to compile the extended example

Usage Output

user@computer:$ ./test_bed --help
This is just a template app that should be modified and added to in order to create a useful command line application

USAGE: test_bed [-hvt] [-w ARG] [-m ARG] -n ARG add like

-- Option Descriptions --

Positional arguments:
add "additional options"
like "this"

Option Arguments:
-h [ --help ] Print help messages
-v [ --verbose ] print words with verbosity
-w [ --word ] words for the sentence, specify multiple times
-t just a temp option that does very little
-n [ --necessary ] give me anything
-m [ --manual ] extract value manually

Example Application


#include "PrettyOptionPrinter.hpp"

#include "boost/program_options.hpp"
#include "boost/filesystem.hpp"

#include <iostream>
#include <string>

namespace
{
  const size_t ERROR_IN_COMMAND_LINE = 1;
  const size_t SUCCESS = 0;
  const size_t ERROR_UNHANDLED_EXCEPTION = 2;

} // namespace

//------------------------------------------------------------------------
int main(int argc, char** argv)
{
  try
  {
    std::string appName = boost::filesystem::basename(argv[0]);
    int add = 0;
    int like = 0;
    std::vector<std::string> sentence;

    /** Define and parse the program options
     */
    namespace po = boost::program_options;
    po::options_description desc("Options");
    desc.add_options()
      ("help,h", "Print help messages")
      ("verbose,v", "print words with verbosity")
      ("word,w", po::value<std::vector<std::string> >(&sentence),
       "words for the sentence, specify multiple times")
      (",t", "just a temp option that does very little")
      ("necessary,n", po::value<std::string>()->required(), "necessary!")
      ("manual,m", po::value<std::string>(), "extract value manually")
      ("add", po::value<int>(&add)->required(), "additional options")
      ("like", po::value<int>(&like)->required(), "this");

    po::positional_options_description positionalOptions;
    positionalOptions.add("add", 1);
    positionalOptions.add("like", 1);

    po::variables_map vm;

    try
    {
      po::store(po::command_line_parser(argc, argv).options(desc)
                  .positional(positionalOptions).run(),
                vm); // throws on error

      /** --help option
       */
      if ( vm.count("help")  )
      {
        std::cout << "This is just a template app that should be modified"
                  << " and added to in order to create a useful command"
                  << " line application" << std::endl << std::endl;
        rad::OptionPrinter::printStandardAppDesc(appName,
                                                 std::cout,
                                                 desc,
                                                 &positionalOptions);
        return SUCCESS;
      }

      po::notify(vm); // throws on error, so do after help in case
                      // there are any problems
    }
    catch(boost::program_options::required_option& e)
    {
      rad::OptionPrinter::formatRequiredOptionError(e);
      std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
      rad::OptionPrinter::printStandardAppDesc(appName,
                                               std::cout,
                                               desc,
                                               &positionalOptions);
      return ERROR_IN_COMMAND_LINE;
    }
    catch(boost::program_options::error& e)
    {
      std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
      rad::OptionPrinter::printStandardAppDesc(appName,
                                               std::cout,
                                               desc,
                                               &positionalOptions);
      return ERROR_IN_COMMAND_LINE;
    }

    // can do this without fear because it is required to be present
    std::cout << "Necessary = "
              << vm["necessary"].as<std::string>() << std::endl;

    if ( vm.count("verbose") )
    {
      std::cout << "VERBOSE PRINTING" << std::endl;
    }
    if (vm.count("verbose") && vm.count("t"))
    {
      std::cout << "heeeeyahhhhh" << std::endl;
    }

    std::cout << "Required Positional, add: " << add
              << " like: " << like << std::endl;

    if ( sentence.size() > 0 )
    {
      std::cout << "The specified words: ";
      std::string separator = " ";
      if (vm.count("verbose"))
      {
        separator = "__**__";
      }
      for(size_t i=0; i<sentence.size(); ++i)
      {
        std::cout << sentence[i] << separator;
      }
      std::cout << std::endl;

    }

    if ( vm.count("manual") )
    {
      std::cout << "Manually extracted value: "
                << vm["manual"].as<std::string>() << std::endl;
    }

  }
  catch(std::exception& e)
  {
    std::cerr << "Unhandled Exception reached the top of main: "
              << e.what() << ", application will now exit" << std::endl;
    return ERROR_UNHANDLED_EXCEPTION;

  }

  return SUCCESS;

} // main

//------------------------------------------------------------------------

24 Responses to “How to use Boost.Program_options”


  1. Hey,

    thanks for posting this! It was extremely useful.

    Ilia Choly
    October 12th, 2012
  2. Very helpful! Thanks for posting this.

    Chris
    April 27th, 2013
  3. Thanks, great post.

    B1
    May 10th, 2013
  4. This is a very nice tutorial, thanks!

    Mikhail
    July 13th, 2013
  5. thanks, great tutorial !

    ilan
    August 3rd, 2013
  6. Great write up!

    Hiranya
    August 23rd, 2013
  7. Although you describe the svn like subcommand syntax (svn commit ), this doesn’t actually show how to implement something like that. It seems like program_options doesn’t really support that, and you’ll have to do a lot of the parsing and help message printing for the subcommands yourself. Am I missing something?

    Jon
    October 24th, 2013
    • Hi Jon,

      You are correct that boost.program_options doesn’t support subcommands “out of the box” and Indeed my selection of using svn commands, like ‘svn commit’, was not intended to open that can of worms. My only goal with the example was to show what a basic positional option looked like (in retrospect this was clearly a sub optimal choice). Now with that aside your question piqued my curiousity about whether it would be possible to get boost.program_options to support sub commands in some way that was not horrible given the additional complexity they introduce. After looking into it I’m convinced that it can be done and I hope to put up some example code in the next couple of weeks.

      Thanks for your interest and for highlighting an erroneous corner in the tutorial.

      radman
      November 15th, 2013
    • For those that are interested I actually ended up writing a subcommand program options library called Oberon. Took me a while longer than I expected but you can now find it here.

      radman
      August 17th, 2014
  8. A quick hack to avoid required positional arguments printed in the options description:

    // Hack: remove the two last positional arguments from options’ description
    typedef vector<boost::shared_ptr > options_vector;
    options_vector& ov = (options_vector&)desc.options();
    ov.pop_back();
    ov.pop_back();

    Pablo
    March 27th, 2014
  9. Hello,
    I thank you for your tutorial which already helped me quite a lot.

    I don’t know if i’m mistaken or simply missing something but in the source code you’re giving, PrettyOptionPrinter doesn’t exist. I had to change the include inside main.cpp and OptionPrinter.cpp to to make the program compile.

    Hope it’ll help you 🙂

    Sum
    April 15th, 2014
    • Hi Sum,

      You are probably not confused, I originally called OptionPrinter PrettyOptionPrinter and I must have forgotten to properly change the #include lines before submitting the code. I will check this out and rectify the problem.

      Thanks!

      radman
      April 17th, 2014
  10. Thanks!

    mamajonok
    May 28th, 2014
  11. When using short name only there is no obvious way to access the option later in variables map.

    Tom
    August 28th, 2014
    • Hi Tom,

      I can’t believe I missed including that in the tutorial, thanks for spotting it! I have now updated the details above to indicate how this is done, the very short answer is that a shorthand only option’s name matches how it would be specified on the command line i.e. -X where X is the character for the option e.g. -a, -o etc.

      radman
      August 28th, 2014
  12. Thanks!

    prepin
    February 17th, 2016
  13. Just a note on the example. I am grooming an example from the Boost C++ Application Cookbook by Anthony Polukhin. I feel that examples like this should be written in their own class. Too many people start coding in main() and this leads down the rabbit hole.

    I am looking at notifiers and I wonder whether it is possible to use more than one description and variables_map in an application. That is just a rhetorical question at the moment. Understanding the use of notify and notifier functors is more important to me at the moment.

    john in newfoundland
    June 15th, 2016
    • Hi John,

      You’re right the program options stuff shown here should be wrapped in a class, this way you can define the config as members of the class and have a defined interface for extracting the values (boost::optional is useful in this regard). I avoided doing this in the example to keep things simple.

      For your second part, I can confirm it is definitely possible to have more than one description and variables_map in an application. If you’re interested in this I suggest you check out my extension library Oberon in which I use this feature extensively. The code for it is not extensive so you should be able to get an idea of how combining variables_map’s etc works pretty quickly.

      radman
      June 23rd, 2016
  14. This is very useful. Thanks very much. Could you consider sharing the code via mercurial/git? I myself prefer hg and don’t use git, so I prefer Bitbucket for such things.

    Arun
    August 23rd, 2016
  15. Very nice tutorial. Thanks!

    huihua
    June 7th, 2018
  16. If the timer not works or the controls are very troublesome to make use of or
    there is not a thermostat, you’ll find that it is
    much easier to opt for a new boiler.

    boiler installation Manual
    December 9th, 2018

Leave a Comment