【问题标题】:Create a prefix command with Boost.Program_options使用 Boost.Program_options 创建前缀命令
【发布时间】:2016-10-26 01:59:15
【问题描述】:

我想使用 Boost.Program_options 创建像 strace 这样的前缀程序。前缀表示我的程序放在另一个任意command [args]前面。因此,我的程序应该接受许多定义的关键字参数/标志。第一个位置参数表示我作为前缀的命令。这个命令本身可能跟在我不知道的任何参数组合之后,并且可能与我的程序的参数重叠。因此,第一个位置参数以及它之后的任何内容都应该以 std::vector<std::string> 结尾:

./foo --bar 13 command1                      # Should run fine
./foo command2 positional                    # Should run fine
./foo --bar 13 command3 --unknown argument   # Should run fine
./foo --unknown command4                     # should fail
./foo --bar 13 command5 --bar 42             # Should work but set bar to 13
./foo command6 --bar 42                      # Should not set bar at all
./foo --bar 13 -- command7 --bar 42 --unknown argument     # Should work

正确的用法是首先指定任何已定义的关键字 基本上在正确的形式中,我定义的第一个数应该取任意数的预定义

我尝试了两种方法:

1) 使用allow_unregistered

#include <iostream>
#include <boost/program_options.hpp>

namespace po = boost::program_options;

int main(int argc, const char** argv)
{
    int bar = 0;
    po::options_description desc("Allowed options");

    desc.add_options()
            ("bar", po::value(&bar), "bar");


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

    auto command = po::collect_unrecognized(parsed.options, po::include_positional);
    std::cout << "bar: " << bar << ", command:";
    for (const auto& c : command) std::cout << " " << c;
    std::cout << std::endl;
}

命令 4、5、6 失败

2) 一个位置选项,出现次数不限

std::vector<std::string> command;

desc.add_options()
        ("bar", po::value(&bar), "bar")
        ("command", po::value(&command));

po::positional_options_description p;
p.add("command", -1);

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

命令 3、5、6 失败。

【问题讨论】:

  • 请详细描述它的工作原理以及应该允许和不允许哪些组合,从这7个例子中不清楚
  • @alexeykuzmin0 请查看我的更新:“前缀意味着我的程序放在另一个任意 command [args] 前面。因此,我的程序应该接受一些定义的关键字参数/标志。第一个位置参数表示我作为前缀的命令。此命令本身后面可能跟着我不知道的任何参数组合,并且可能与我的程序的参数重叠。因此,第一个位置参数以及它之后的任何内容都应该结束在std::vector&lt;std::string&gt;"中。每个示例都代表特定的极端情况。
  • 参数可以包含空格吗?在这种情况下,参数是否放在“”中?
  • @alexeykuzmin0 这样它应该与正常的参数解析没有区别。参数只能在用“引号”括起来时包含空格。

标签: c++ boost command-line-arguments boost-program-options


【解决方案1】:

在 Boost Trac 上有一个功能请求,包括补丁:https://svn.boost.org/trac/boost/ticket/6991。票证上没有太大变动,但补丁仍然完全适用于 Boost(截至 1.61.0,最新版本)。

如果您的构建系统允许,您可以将补丁应用到您的本地 Boost 副本;否则,您可以从 cmdline.hppdetail/cmdline.hppcmdline.cpp 中提取 boost::program_options::detail::cmdline 到您自己的命名空间中,修补该组件,然后使用您修补的组件代替 boost::program_options::cmdline

另一种选择是破解boost::program_options::detail::cmdline 的行为,使用其extra_style_parser 参数:

po::detail::cmdline cmdline(argc, argv);
cmdline.set_options_description(desc);
cmdline.set_positional_options(p);
std::vector<po::detail::cmdline::style_parser> style_parsers{
    [&](auto& args) { return cmdline.parse_long_option(args); },
    [&](auto& args) { return cmdline.parse_short_option(args); }};
cmdline.extra_style_parser([&](std::vector<std::string>& args) {
    auto const current_size = args.size();
    std::vector<po::option> result;
    for (auto const& parser : style_parsers) {
        auto const next = parser(args);
        result.insert(result.end(), next.begin(), next.end());
        if (args.size() != current_size)
            return result;
    }
    if (args.size() && args[0] != "--") args.insert(args.begin(), "--");
    auto const next = cmdline.parse_terminator(args);
    result.insert(result.end(), next.begin(), next.end());
    return result;
});
po::parsed_options parsed{&desc};
parsed.options = cmdline.run();
po::store(parsed, vm);
po::notify(vm);

Example警告:这是对未记录的库内部的黑客行为,随时可能中断。

【讨论】:

  • 看起来这正是我所需要的。很遗憾看到这个补丁从未起飞。我会看看它是否适合我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-09
  • 2021-09-11
  • 2013-12-30
  • 1970-01-01
  • 2021-02-07
相关资源
最近更新 更多