1

I am attempting to create a program with the following command line usage:

test_program [General Options] <mode_1_option_1> <required_option_1> 

OR

test_program [General Options] --toggle-mode <mode_2_option_1> <mode_2_option_2> <mode_2_option_3> <required_option_1> 

Where the inclusion of '--toggle-mode' dictates which mode the program runs in.


The following is my attempt at implementing Boost::program_options to accomplish this:

namespace po = boost::program_options; bool use_mode_2 = false; po::options_description general_options_desc("General Options"); general_options_desc.add_options() ("help,h", "this help message"); po::options_description mode_options_desc("Mode options"); mode_options_desc.add_options() ("toggle-mode,M", po::bool_switch(&use_mode_2), "specify mode 2 operations"); po::options_description required_options_desc("Required Options"); required_options_desc.add_options() ("required_option_1", po::value<std::string>()->required()); po::options_description mode_1_options_desc("Mode #1 Options"); mode_1_options_desc.add_options() ("mode_1_option_1", po::value<std::string>()->required()); po::options_description mode_2_options_desc("Mode #2 Options"); mode_2_options_desc.add_options() ("mode_2_option_1", po::value<std::string>()->required()) ("mode_2_option_2", po::value<std::string>()->required()) ("mode_2_option_3", po::value<std::string>()->required()); /* Sets my "use_mode_2" variable */ { po::variables_map mode_vm; po::store(po::command_line_parser(argc, argv).options(mode_options_desc).allow_unregistered().run(), mode_vm); po::notify(mode_vm); } po::options_description visible_options_desc("Visible Options"); visible_options_desc.add(general_options_desc); visible_options_desc.add(mode_options_desc); po::options_description all_options_desc("All Options"); all_options_desc.add(visible_options_desc); int positional_index = 1; po::positional_options_description positional_options_desc; if (use_mode_2) { positional_options_desc.add("mode_2_option_1", positional_index++); positional_options_desc.add("mode_2_option_2", positional_index++); positional_options_desc.add("mode_2_option_3", positional_index++); all_options_desc.add(mode_2_options_desc); } else { positional_options_desc.add("mode_1_option_1", positional_index++); all_options_desc.add(mode_1_options_desc); } positional_options_desc.add("required_option_1", positional_index); all_options_desc.add(required_options_desc); po::variables_map vm; po::store(po::command_line_parser(argc, argv).options(all_options_desc).positional(positional_options_desc).run(), vm); if (vm.count("help")) { fprintf(stderr, "Usage:\n"); fprintf(stderr, " test_program [General Options] <mode_1_option_1> <required_option_1>\n"); fprintf(stderr, " test_program [General Options] --toggle-mode <mode_2_option_1> <mode_2_option_2> <mode_2_option_3> <required_option_1>\n\n"); visible_options_desc.print(std::cout); return EXIT_SUCCESS; } 

Running my program in Mode #1 works as expected:

.\test_program mode_1_option_1 required_option_1 

But I run into issues while running it in Mode #2:

.\test_program -M mode_2_option_1 mode_2_option_2 mode_2_option_3 required_option_1 std_exception: option '--mode_2_option_2' cannot be specified more than once 

Does anyone have any idea what I may be missing?

Thanks!

1 Answer 1

1

At the core is the problem that positional_options_descriptions::add does not take a "position index". It takes a max_count!

In your case, it should be 1 always. If you want to actually take multiple, the underlying option should support it:

("mode_2_option_2", po::value<std::vector<std::string>>()->required()) 

Compare

Live On Coliru

#include <boost/program_options.hpp> #include <iomanip> #include <iostream> namespace po = boost::program_options; int main(int argc, char* argv[]) { bool modeB = false; po::options_description general_options_desc("General Options"); general_options_desc.add_options() ("help,h", "this help message"); po::options_description mode_options_desc("Mode options"); mode_options_desc.add_options() ("modeB,B", po::bool_switch(&modeB), "specify mode 2 operations"); po::options_description required("Required Options"); required.add_options() ("req_last1", po::value<std::string>()->required()); { /* Set modeB */ po::variables_map mode_vm; store(po::command_line_parser(argc, argv) // .options(mode_options_desc) .allow_unregistered() .run(), mode_vm); notify(mode_vm); } po::options_description visible_options_desc("Visible Options"); visible_options_desc.add(general_options_desc); visible_options_desc.add(mode_options_desc); po::options_description optsA("Mode #1 Options"); optsA.add_options() ("A1", po::value<std::string>()->required()); po::options_description optsB("Mode #2 Options"); optsB.add_options() ("B1", po::value<std::string>()->required()) ("B2", po::value<std::vector<std::string>>()->required()) ("B3", po::value<std::string>()->required()); po::options_description all_options_desc("All Options"); all_options_desc.add(visible_options_desc); po::positional_options_description positionals; if (!modeB) { positionals.add("A1", 1); all_options_desc.add(optsA); } else { positionals.add("B1", 1); positionals.add("B2", 1); positionals.add("B3", 1); all_options_desc.add(optsB); } positionals.add("req_last1", 1); { std::cout << "positional(" << positionals.max_total_count() << "):"; for (size_t i = 0; i < positionals.max_total_count(); i++) std::cout << " " << positionals.name_for_position(i); std::cout << "\n"; } all_options_desc.add(required); po::variables_map vm; store(po::command_line_parser(argc, argv) // .options(all_options_desc) .positional(positionals) .run(), vm); if (vm.count("help")) { fprintf(stderr, "Usage:\n"); fprintf(stderr, " test_program [General Options] <A1> <req_last1>\n"); fprintf(stderr, " test_program [General Options] --modeB <B1> <B2> <B3> <req_last1>\n\n"); if (modeB) { optsB.print(std::cout); } else { optsA.print(std::cout); } visible_options_desc.print(std::cout); return 0; } for (auto [k, v] : vm) { if (v.value().type() == typeid(std::string)) { std::cout << k << ": " << quoted(v.as<std::string>()) << "\n"; } else if (v.value().type() == typeid(bool)) { std::cout << k << ": " << std::boolalpha << v.as<bool>() << "\n"; } else if (v.value().type() == typeid(std::vector<std::string>)) { std::cout << k << ":"; for (auto& s : v.as<std::vector<std::string>>()) std::cout << " " << quoted(s); std::cout << "\n"; } else { std::cout << k << ": " << "???\n"; } } } 

Printing e.g.

Now, make that

 positionals.add("B2", 3); // expect B2 three time 

Now we see Live On Coliru

$ ./a.out foo this is really too much -B positional(6): B1 B2 B2 B2 B3 req_last1 B1: "foo" B2: "this" "is" "really" B3: "too" modeB: true req_last1: "much" 
Sign up to request clarification or add additional context in comments.

1 Comment

Ah, thanks so much! You've helped me a ton.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.