Search code examples
c++boostboost-program-options

Best way of handling level 2 commands with Boost ProgramOptions


I'm interested in having a single executable that takes level 2 commands on the command line - kind of like git commit and git add are their own separate commands within one EXE. So my question is: is there any way of simplifying this with ProgramOptions? I know I can define different schemes and then check argv[1] against a particular string, but I'm hoping there is a cleaner way. Thanks!


Solution

  • You can use positional parameters:

    #include <boost/program_options.hpp>
    
    #include <iostream>
    #include <algorithm>
    #include <iterator>
    
    namespace po = boost::program_options;
    
    // A helper function to simplify the main part.
    template<class T>
    std::ostream& operator<<(std::ostream& os, const std::vector<T>& v)
    {
      copy(v.begin(), v.end(), std::ostream_iterator<T>(os, " "));
      return os;
    }
    
    int main(int ac, char* av[])
    {
      try
        {
          po::options_description desc("Allowed options");
          desc.add_options()
            ("help", "produce help message")
            ("command", po::value<std::string>(), "command to execute")
            ("command_opt", po::value<std::vector<std::string> >(), "command options")
            ;
    
          po::positional_options_description p;
          p.add("command", 1);
          p.add("command_opt", -1);
    
          po::variables_map vm;
          po::store(po::command_line_parser(ac, av).
                    options(desc).positional(p).run(), vm);
          po::notify(vm);
    
          if (vm.count("help")) {
            std::cout << "Usage: options_description [options]\n";
            std::cout << desc;
            return 0;
          }
    
          if (vm.count("command"))
            {
              std::cout << "command: " << vm["command"].as<std::string>() << "\n";
            }
    
          if (vm.count("command_opt"))
            {
              std::cout << "command options: " << vm["command_opt"].as<std::vector<std::string> >() << "\n";
            }
    
        }
      catch(std::exception& e)
        {
          std::cout << e.what() << "\n";
          return 1;
        }
    }
    

    But AFAIK you'll have to do the logic checks of which option is compatible with which other option yourself as there is no dependency system in boost program options.

    Grouping each command options in a different option group as described here might help for the generated usage text.