Question

I am trying to use a class to dynamically assemble program options for different applications. When using plain pointers for desc everything works fine. In the following case with shared_ptr the parser does not recognize the added program options.

What am I missing?

Thanks a lot.

namespace config = boost::program_options;

class Parser {

public:

  Parser();

  void add_all();
  void add_x_options();
  void add_y_options();
  void add_z_options();

  void parse(int argc, char** argv);

  std::shared_ptr<boost::program_options::options_description> desc;

};


Parser::Parser() {
  desc = std::shared_ptr<boost::program_options::options_description>(new boost::program_options::options_description("Options"));
}


void Parser::add_x_options() {
  ...
  desc->add_options()
    ("option1", config::value<std::string>()->required())
    ("option2", config::value<std::string>()->required());
}

void Parser::parse(int argc, char** argv) {
  config::variables_map vmap;

  try {
    config::store(config::parse_command_line(argc, argv, *desc), vmap);

    if (vmap.count("help")) {
      std::cout << *desc << std::endl;
      return false;
    }

    vmap.notify();
  }
  catch(std::exception& e) {
    std::cout << "Error message.\n";
    return false;
  }
  return true;
}
Était-ce utile?

La solution

I don't know what you're missing, but it is probably not in the code shown. Unless you actually forget to call add_x_options() in time?

Here's my test program: See it Live On Coliru

int main()
{
    do_test({ "--help" });
    do_test({ "--option1" });
    do_test({ "--option1", "value1" });
    do_test({ "--option2", "value2" });
    do_test({ "--option1", "value1", "--option2", "value2" });
}

Which outputs (I added the --help option in add_x_options so it could be tested):

---------------------------------------------------------------------------
Test: fake_program.exe --help 
without add_x_options: Error message: unrecognised option '--help'
after add_x_options: Options:
  --help                print usage instructions
  --option1 arg
  --option2 arg

---------------------------------------------------------------------------
Test: fake_program.exe --option1 
without add_x_options: Error message: unrecognised option '--option1'
after add_x_options: Error message: the required argument for option '--option1' is missing
---------------------------------------------------------------------------
Test: fake_program.exe --option1 value1 
without add_x_options: Error message: unrecognised option '--option1'
after add_x_options: Error message: the option '--option2' is required but missing
---------------------------------------------------------------------------
Test: fake_program.exe --option2 value2 
without add_x_options: Error message: unrecognised option '--option2'
after add_x_options: Error message: the option '--option1' is required but missing
---------------------------------------------------------------------------
Test: fake_program.exe --option1 value1 --option2 value2 
without add_x_options: Error message: unrecognised option '--option1'
after add_x_options: 

Full Code

#include <boost/program_options.hpp>
#include <boost/config.hpp>
#include <memory>
#include <iostream>

class Parser {
    public:
        Parser();
        void add_all();
        void add_x_options();
        void add_y_options();
        void add_z_options();
        bool parse(int argc, char** argv);
    private:
        std::shared_ptr<boost::program_options::options_description> desc;
};


Parser::Parser() {
    desc = std::shared_ptr<boost::program_options::options_description>(new boost::program_options::options_description("Options"));
}

namespace config = boost::program_options;

void Parser::add_x_options() {
    //...
    desc->add_options()
        ("help",    "print usage instructions")
        ("option1", config::value<std::string>()->required())
        ("option2", config::value<std::string>()->required());
}

bool Parser::parse(int argc, char** argv) {
    config::variables_map vmap;

    try {
        config::store(config::parse_command_line(argc, argv, *desc), vmap);

        if (vmap.count("help")) {
            std::cout << *desc << std::endl;
            return false;
        }

        vmap.notify();
        return true;
    }
    catch(std::exception& e) {
        std::cout << "Error message: " << e.what() << "\n";
        return false;
    }
}

void do_test(std::initializer_list<const char*> args)
{
    std::string fake_program = "fake_program.exe";
    std::vector<char*> fake_argv { &fake_program[0] };
    for(auto&& arg : args)
        fake_argv.push_back(const_cast<char*>(arg));

    Parser parser;

    std::cout << "---------------------------------------------------------------------------\n";
    std::cout << "Test: ";
    for(auto const& a : fake_argv)
        std::cout << a << " ";
    std::cout << "\n";

    std::cout << "without add_x_options: ";
    parser.parse(fake_argv.size(), fake_argv.data());

    std::cout << "after add_x_options: ";
    parser.add_x_options();
    parser.parse(fake_argv.size(), fake_argv.data());
}

int main()
{
    do_test({ "--help" });
    do_test({ "--option1" });
    do_test({ "--option1", "value1" });
    do_test({ "--option2", "value2" });
    do_test({ "--option1", "value1", "--option2", "value2" });
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top