我正在尝试使用以下命令行用法创建一个程序:
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
有人知道我可能缺少什么吗?
谢谢!
核心是
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"