Search code examples
c++boostshared-ptrboost-program-options

Boost program options and shared_ptr


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;
}

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" });
    }