2

I have a program that use boost::program_options to parse a command line. One of the parameter is the name of an AMQP exchange, and a default value is provided. For testing purposes, I would like to override this AMQP exchange name with an empty string (to use a default exchange).

I don't know how to pass an empty string to boost::program_options. Is that possible? Without modifying the source code? If not, what do you recommend?

Here is a minimum working example code :

#include <boost/program_options.hpp>

namespace po = boost::program_options;

constexpr auto EXECUTABLE_DESCRIPTION = 
"Minimum working example showing my difficulties while trying to " 
"pass an empty string as an argument to boost::program_options.";

int main(int argc, char **argv) {

  std::string elkExchange;
  po::options_description config("");
  config.add_options()
          ("amqp.exchange",
          po::value<std::string>(&elkExchange)  
          ->default_value("default-exchange"),  // default value used in prod
          "Exchange on which to send the events");

  // do the parsing 
  po::options_description cmdline_options(std::string{EXECUTABLE_DESCRIPTION});
  cmdline_options.add(config);
  po::variables_map args;
  store(po::command_line_parser(argc, argv).
          options(cmdline_options).run(), args);
  notify(args);

  // debug display 
  std::cout << "Send event on elk.exchange: {" << elkExchange << "}" << std::endl;

  // real application code here ...

  return EXIT_SUCCESS;
}

Here is what I would like to do:

# default value is used as expected
$ ./boost/empty-string-in-cli/exec
Send event on elk.exchange: {default-exchange}

# override with non-empty value work as expected
$ ./boost/empty-string-in-cli/exec --amqp.exchange='custom'
Send event on elk.exchange: {custom}

# here is where the troubles begin
$ ./boost/empty-string-in-cli/exec --amqp.exchange=''
terminate called after throwing an instance of 'boost::wrapexcept<boost::program_options::invalid_command_line_syntax>'
  what():  the argument for option '--amqp.exchange' should follow immediately after the equal sign
Aborted (core dumped)

Thanks in advance : )

GabrielGodefroy
  • 198
  • 1
  • 8
  • Did you try using an `implicit_value` as suggested [here](https://stackoverflow.com/a/1809788/2169513)? – user2169513 Aug 31 '22 at 10:17
  • I could use implicit_value indeed. That that will leave it to the caller to handle the empty string case: `./exec` will use the __default__ value, `./exec --amqp.exchange` the __implicit__ one and `./exec --amqp.exchange='custom'` the __provided__ one. Ideally, I would like to do is `./exec --amqp.exchange='$EXCHANGE'` where EXCHANGE is potentially empty Still, this is my favorite option so far as it is backward compatible as uses boost::program_options only. Thanks! – GabrielGodefroy Aug 31 '22 at 11:41
  • `amqp.exchange='$EXCHANGE'` would not expand `$EXHANGE` on any posix-compatible shell. Maybe you are using somthing unique, or meant `"$EXCHANGE"`? – sehe Aug 31 '22 at 12:17
  • Indeed yes, I wanted to use double quotes. For information, I think I will go for the `implicit_value` solution and modify my launching bash scripts to use `./exec --elk.enable${EXCHANGE:+"= $EXCHANGE"}`. That work in my case only because there is no implicit_value yet, and I can modify the executable, but that should do the trick. – GabrielGodefroy Aug 31 '22 at 12:48

1 Answers1

1

To my surprise, the adjacent long option parser absolutely forbids the = sign with a zerolength value, see the code on https://github.com/boostorg/program_options/blob/develop/src/cmdline.cpp#L520.

Therefore it seems your only recourse is to avoid using the adjacent-value syntax:

for EXCHANGE in foo bar ''; do ./build/sotest --amqp.exchange "$EXCHANGE"; done

Prints

Send event on elk.exchange: {foo}
Send event on elk.exchange: {bar}
Send event on elk.exchange: {}
sehe
  • 374,641
  • 47
  • 450
  • 633