Search code examples
c++boost-program-options

I am unable to emulate subcommands using positional options followed by unregistered_options


I am trying to emulate something like this: apple --color red --count 5 orange --taste sour

Where I intend to make apple and orange as positional_option: subCommand and I intend to capture the remainder of options with values as unregistered options. Here is my sample code:

    po::options_description desc("Supported options");
     desc.add_options()
       ("subCom",po::value<string>(&sc),"")
       ;
    po::positional_options_description pos;
    pos.add("subCom",1);

    po::variables_map vm;
    po::parsed_options parsed =
       po::command_line_parser(argc, argv).options(desc).positional(pos).allow_unregistered().run();
    po::store(parsed, vm);
    po::notify(vm);

Now when I actually run this: apple --color red --count 5

I am getting the error: too many positional options. program options is considering red as a positional option instead of treating it as a value for unregistered option. Could you please suggest a work around to this problem?


Solution

  • Try this (C++11, but if you wish you may to downgrade it easily)

    
    #include <vector>
    #include <string>
    
    #include <boost/program_options.hpp>
    
    namespace po = boost::program_options;
    using namespace std;
    
    int main(int argc, char* argv[]) {
        po::options_description visibleOpts;
        visibleOpts.add_options()
            ("help,h", "displays this help")
            ("verbose,v", po::value()->zero_tokens(), "increase debug level")
            ("silent,s", po::value()->zero_tokens(), "decrease debug level")
        ;
        po::options_description hiddenOpts("");
        vector files;
        hiddenOpts.add_options()
            ("input-file", po::value >(&files)->required());
        po::options_description opts;
        opts.add(visibleOpts).add(hiddenOpts);
    
        po::positional_options_description pos;
        pos.add("input-file", -1);
    
        // FIXME wrap this line with a proper try/catch
        po::command_line_parser clp(argc, argv);
        clp.options(visibleOpts).options(opts).positional(pos).allow_unregistered();
        po::variables_map vm;
        auto parsed = clp.run();
        po::store(parsed, vm);
        po::notify(vm);
        if (vm.count("silent"))
            cout << "silent was used" << endl;
        if (vm.count("verbose"))
            cout << "verbose was used" << endl;
        {
            vector unrecognized =
                po::collect_unrecognized(parsed.options, po::exclude_positional);
            if (!unrecognized.empty()) {
                cout << "Got " << unrecognized.size() << " unrecognized option(s):" << endl;
                for(auto uo: unrecognized)
                    cout << '\t' << uo << endl;
            }
        }
        if (!files.empty()) {
            cout << "Got " << files.size() << " file(s):" << endl;
            for(auto f: files)
                cout << '\t' << f << endl;
        }
        return 0;
    }
    

    it gives me the following output:

    alex@galene c++/tests/build $ ./boost_po_pos --silent --boo abcde ssss
    silent was used
    Got 1 unrecognized option(s):
            --boo
    Got 2 file(s):
            abcde
            ssss
    alex@galene c++/tests/build $