我正在使用 boost::program_options 使用以下通用语法制作一个简单的可执行文件:
program --command <cmd> [args]
例如:
program --command run --listen locahost --port 8080
我想让
command
部分成为位置参数,这样用户就不会被迫每次都键入它。因此它也应该像这样工作:
program run --listen locahost --port 8080
每个命令都有自己的一组参数,并且所有参数都是可选的。
以下代码仅在不使用位置参数时才能正常运行。根据 boost 文档,这看起来很好(除非我遗漏了一点)。添加位置参数时,我在解析时遇到异常,显示
too many positional options have been specified on the command line
// Define the options
po::options_description args("Generic options");
args.add_options()
("command", po::value<std::string>(), "command to execute");
("arg1", po::value<std::string>(), "argument 1")
("arg2", po::value<std::string>(), "argument 2")
("arg3", po::value<std::string>(), "argument 3")
("arg4", po::value<std::string>(), "argument 4");
po::positional_options_description positional;
positional.add("command", 1);
po::variables_map vm;
po::store(po::command_line_parser(argc, argv).options(args)
.positional(positional) // Problem here
.allow_unregistered()
.run(), vm);
// Check if positional command is present
if (vm.count("command")) {
std::string command = vm["command"].as<std::string>();
std::cout << "Command: " << command << "\n";
}
po::notify(vm);
std::cout << "arg1: " << vm["arg1"].as<std::string>() << "\n";
std::cout << "arg2: " << vm["arg2"].as<std::string>() << "\n";
std::cout << "arg3: " << vm["arg3"].as<std::string>() << "\n";
std::cout << "arg4: " << vm["arg4"].as<std::string>() << "\n";
就像评论者可能想要的那样,未知选项
--listen
、locahost
、--port
和 8080
被视为逐字位置参数。仅定义了一个。
但是,您的问题文本和代码是相互矛盾的
您的文字描述表明您希望在没有未注册选项的情况下正常工作:
#include <boost/program_options.hpp>
#include <iostream>
namespace po = boost::program_options;
int main(int argc, char* argv[]) {
// Define the options
po::options_description args("Generic options");
args.add_options() //
("command", po::value<std::string>(), "command to execute") //
("listen", po::value<std::string>(), "bind address") //
("port", po::value<std::string>(), "TCP port") //
;
po::positional_options_description positional;
positional.add("command", 1);
po::variables_map vm;
store(po::command_line_parser(argc, argv)
.options(args)
.positional(positional)
//.allow_unregistered()
.run(),
vm);
notify(vm);
// Check if positional command is present
if (vm.contains("command")) {
std::string command = vm["command"].as<std::string>();
std::cout << "Command: " << command << "\n";
}
if (vm.contains("listen"))
std::cout << "listen: " << vm["listen"].as<std::string>() << "\n";
if (vm.contains("port"))
std::cout << "port: " << vm["port"].as<std::string>() << "\n";
}
印刷
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_program_options && ./a.out run --listen locahost --port 8080
Command: run
listen: locahost
port: 8080
另一方面,您的代码表明您想走另一条路:全部采用位置参数。如果您不打算自己使用任何长选项,这可以工作:
#include <boost/program_options.hpp>
#include <iostream>
namespace po = boost::program_options;
int main(int argc, char* argv[]) {
// Define the options
po::options_description args("Generic options");
args.add_options() //
("command", po::value<std::string>(), "command to execute") //
("arg1", po::value<std::string>(), "argument 1") //
("arg2", po::value<std::string>(), "argument 2") //
("arg3", po::value<std::string>(), "argument 3") //
("arg4", po::value<std::string>(), "argument 4") //
;
po::positional_options_description positional;
positional.add("command", 1);
positional.add("arg1", 1);
positional.add("arg2", 1);
positional.add("arg3", 1);
positional.add("arg4", 1);
po::variables_map vm;
auto style = po::command_line_style::default_style & ~po::command_line_style::allow_long;
po::store(po::command_line_parser(argc, argv)
.options(args)
.style(style)
.positional(positional)
.run(),
vm);
po::notify(vm);
// Check if positional command is present
if (vm.count("command"))
std::cout << "Command: " << vm["command"].as<std::string>() << "\n";
if (vm.contains("arg1"))
std::cout << "arg1: " << vm["arg1"].as<std::string>() << "\n";
if (vm.contains("arg2"))
std::cout << "arg2: " << vm["arg2"].as<std::string>() << "\n";
if (vm.contains("arg3"))
std::cout << "arg3: " << vm["arg3"].as<std::string>() << "\n";
if (vm.contains("arg4"))
std::cout << "arg4: " << vm["arg4"].as<std::string>() << "\n";
}
哪个打印:
Command: run
arg1: --listen
arg2: locahost
arg3: --port
arg4: 8080
按照这个速度,我会在没有程序选项的情况下进行简化:
#include <iomanip>
#include <iostream>
#include <string_view>
#include <vector>
int main(int argc, char* argv[]) {
std::vector<std::string_view> vm(argv+1, argv+argc);
enum { command, arg1, arg2, arg3, arg4 };
std::cout << "Command: " << quoted(vm.at(command)) << "\n";
std::cout << "Arg1: " << quoted(vm.at(arg1)) << "\n";
std::cout << "Arg2: " << quoted(vm.at(arg2)) << "\n";
std::cout << "Arg3: " << quoted(vm.at(arg3)) << "\n";
std::cout << "Arg4: " << quoted(vm.at(arg4)) << "\n";
}
打印
Command: "run"
Arg1: "--listen"
Arg2: "locahost"
Arg3: "--port"
Arg4: "8080"
或者,考虑使用解析器框架从原始令牌流解析命令,例如振奋精神