Boost::program_options 在实现具有多种模式的程序时出现“选项不能被指定多次”错误

问题描述 投票:0回答:1

我正在尝试使用以下命令行用法创建一个程序:

test_program [General Options] <mode_1_option_1> <required_option_1>

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

包含“--toggle-mode”指示程序运行的模式。


以下是我尝试实现 Boost::program_options 来实现此目的:

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

在模式 #1 下运行我的程序按预期工作:

.\test_program mode_1_option_1 required_option_1 

但是我在模式 #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

有人知道我可能缺少什么吗?

谢谢!

c++ boost c++20 command-line-arguments boost-program-options
1个回答
0
投票

核心是

positional_options_descriptions::add
采取“位置索引”的问题。需要
max_count

就您而言,它应该始终为 1。如果你想实际获取多个,底层选项应该支持它:

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

比较

住在科里鲁

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

打印例如

现在就做吧

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

现在我们看到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"
© www.soinside.com 2019 - 2024. All rights reserved.