Search code examples
c++boostboost-program-options

Boost program_options: allow undeclared from environment


I have an application that loads options in two steps. In the second step, some options may be undeclared in the options_description object (depending on what options were passed in the first step).

There seems to be no option to ignore undeclared, similar to the ones that exist for parsing command line arguments and configuration files.

A minimal working example:

#include <boost/program_options.hpp>

namespace po = boost::program_options;

int main() {
  int opt1;

  po::options_description options("my app options");
  options.add_options()
    ("opt1", po::value<int>(&opt1)->default_value(0), "option 1");

  po::variables_map env;
  po::store(po::parse_environment(options, "MYAPP_"), env);
  po::notify(env);

  printf("opt1: %d\n", opt1);
  return 0;
}

By default you'll get a value of zero, as expected:

$ ./a.out
opt1: 0

If you set MYAPP_OPT1, it also works as expected

$ MYAPP_OPT1=123 ./a.out
opt1: 123

However, if you set a variable that is not specified in the configuration file, it will crash.

$ MYAPP_UNDEFINED=456 ./a.out
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::program_options::unknown_option> >'
  what():  unrecognised option
Aborted (core dumped)

Sidenote

There's also another annoying thing -- if instead of declaring "opt1" I declare "OPT1", it won't even recognize MYAPP_OPT1!


Solution

  • IMHO this would be a nice addition to the boost library, making it on-par with the command-line and configuration-file parsers.

    The following is adapted from my gist.


    When using Boost's program_options to load environment variables, there is no way of telling boost to ignore those that were not specified in the options_description object.

    This snippet shows how to get similar behavior to the allow_unregistered() option available to command line and configuration file parsers.

    For example, if you try to read MYAPP_ONE by specifying an option called one and using prefix MYAPP in parse_environment, it will work just fine.

    The issue arises if there is another MYAPP_X that wasn't included in the options_description object. Then an exception will be thrown.

    If you use the program arguments (argc, argv) or a configuration file, there is an option in the library to simply ignore the options that weren't declared in the options_description.

    For environment variables, that is not the case. This snippet will simply read the options you declared and will ignore any others that may have been declared.

    Thus, behaving like the allow_unregistered() functionality.

    po::options_description options("My Options");
    lib_options.add_options()
      (
        "MY_VAR",
        po::value<bool>(&my_var)->default_value(false),
        "Dummy boolean test"
      )
    ;
    
    po::variables_map env;
    po::store(po::parse_environment(
      options,
      [options](const std::string& var) {
        return std::any_of(
          options.options().cbegin(),
          options.options().cend(),
          [var](auto opt) { return var == opt->long_name(); }) ? var : "";
    }), env);
    po::notify(env);