【问题标题】:Using '--' as end-of-options marker with boost::program_options使用 '--' 作为带有 boost::program_options 的选项结束标记
【发布时间】:2011-03-29 16:17:47
【问题描述】:

指示命令行程序选项结束的传统方式是使用选项--。如何让 boost::program_options 将其识别为选项并接受命令行的其余部分作为位置参数?以下不起作用:

namespace po = boost::program_options;

po::positional_options_description posOpts;
posOpts.add("keywords", 1);
posOpts.add("input", 1);

std::vector<std::string> final_args;

po::options_description desc("Allowed Options");
desc.add_options()
  ...
  ("", po::value< std::vector<std::string> >(&final_args)->multitoken(), "end of options")
  ...
  ;

po::command_line_parser(argc, argv).options(desc).positional(posOpts).run();

如果我给foo bar 作为参数,我在final_args 中什么也得不到(如预期的那样),而且当我给-- foo bar 作为参数时(当我希望找到final_args[0] == "foo"final_args[1] == "bar" 时)。我在这里假设-- 是一个长参数,其参数名称为空字符串。相反,如果它应该被解释为一个短参数,- 作为参数名称,我该如何指定呢?据我所知,将参数规范从 "" 更改为 ",-" 不会影响结果。

如何让 boost::program_options 正确处理--

编辑:这是尝试通过创建extra_style_parser 来执行 Tim Sylvester 建议的操作:

std::vector<po::option> end_of_opts_parser(std::vector<std::string>& args) {
  std::vector<po::option> result;

  std::vector<std::string>::const_iterator i(args.begin());
  if (i != args.end() && *i == "--") {
    for (++i; i != args.end(); ++i) {
      po::option opt;
      opt.string_key = "pargs";
      opt.original_tokens.push_back(*i);
      result.push_back(opt);
    }

    args.clear();
  }

  return result;
}

"pargs" 被添加到这样的选项中:

("pargs", po::value< std::vector<std::string> >(&pargs), "positional arguments")

在参数列表中使用-- 运行它会导致required_option 异常。 (如果不是为每个尾随 arg 制作一个 po::option,而是将它们全部打包到一个 po::option::original_tokens 中,我会得到类似的结果 po::option。)

【问题讨论】:

  • 命令行中--后面会出现什么?
  • 在我的特定情况下,它将介于零和两个文件名之间,包括在内。我有一个以连字符开头的文件并非不可能。

标签: c++ boost-program-options


【解决方案1】:

有一个简单但不令人满意的解决方法:在将argv 移交给command_line_parser 之前,检查其中是否出现--。如果是这样,请将argc 重置为-- 的位置以隐藏它以及command_line_parser 后面的参数。然后,完成解析后,手动处理-- 之后的位置参数。废话!

【讨论】:

    【解决方案2】:

    我有点困惑,因为 boost::program_options 已经自己完成了这一切:

    $ ./testprog --the-only-option=23 aa --unknown bb
    terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::program_options::unknown_option> >'
      what():  unknown option unknown
    Aborted
    
    $ ./testprog --the-only-option=23 -- aa --unknown bb
    the only option: 23
    positional: aa
    positional: --unknown
    positional: bb
    

    程序:

    #include <boost/program_options.hpp>
    
    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    namespace po = boost::program_options;
    
    static bool handle_command_line(int argc, char *argv[])
    {
        po::options_description desc("Allowed options");
        desc.add_options()
            ("help", "Describe command line options")
            ("the-only-option", po::value<string>(), "some option")
            ;
    
        po::options_description hidden;
        hidden.add_options()
            ("positional", po::value<vector<string> >())
            ;
    
        po::options_description all_options;
        all_options.add(desc).add(hidden);
    
        po::positional_options_description p;
        p.add("positional", -1);
    
        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv).
                  options(all_options).positional(p).run(), vm);
        po::notify(vm);
    
        if (vm.count("help")) {
            cout << "Usage: " << argv[0] << " [options] [args]" << endl;
            cout << desc << "\n";
            return false;
        }
    
        if (vm.count("the-only-option"))
            cout << "the only option: " << vm["the-only-option"].as<string>() << endl;
    
        if (vm.count("positional")) {
            const vector<string> &v = vm["positional"].as<vector<string> >();
            vector<string>::const_iterator it = v.begin();
            for (; it != v.end(); ++it)
                cout << "positional: " << *it << endl;
        }
    
        return true;
    }
    
    int main(int argc, char *argv[])
    {
        if (!handle_command_line(argc, argv))
            return 1;
        return 0;
    }
    

    【讨论】:

    • 如果你已经有其他位置选项,我不相信这会起作用——至少我不能让它起作用。
    【解决方案3】:

    我也有同样的问题,但放弃了。

    我相信这样做的方法是调用program_options::command_line_parser::extra_style_parser(),传递给它一个函数,该函数通过引用获取一个字符串向量并返回一个options 向量(参见cmdline.hpp 中的style_parser typedef) .

    您的函数将需要检测第一个标记是“--”,创建一个新的 option 对象,将输入中的所有其余标记放入 option 的值向量中并清空输入向量。请参阅 libs/program_options/src/cmdline.cpp 中的 program_options::detail::cmdline::parse_long_option 等,了解开始的内容。

    您可能需要注册一个要使用的特定选项值,以便在解析结束时轻松找到这个特殊的option 对象并从中提取一组额外的非选项参数。

    我希望我能给你一些代码,但我从来没有真正做到这一点,我最终只是在标准输入上逐行获取附加参数。

    编辑:

    我很难为你指出一个没有解决问题的方向,所以我回去让它工作。问题是您的位置参数条目没有设置为接受多个标记,并且您没有填写valueprogram_options 代码需要两者,否则它不起作用。

    以下是适合我的完整代码:

    #include <boost/program_options.hpp>
    #include <iostream>
    
    namespace po = boost::program_options;
    typedef std::vector<std::string> stringvec;
    
    std::vector<po::option> end_of_opts_parser(stringvec& args) {
      std::vector<po::option> result;
      stringvec::const_iterator i(args.begin());
      if (i != args.end() && *i == "--") {
        for (++i; i != args.end(); ++i) {
          po::option opt;
          opt.string_key = "pargs";
          opt.value.push_back(*i);      //  <== here
          opt.original_tokens.push_back(*i);
          result.push_back(opt);
        }
        args.clear();
      }
      return result;
    }
    
    int main(int argc, char* argv[])
    {
        po::options_description desc("Allowed Options");
        desc.add_options()
            ("help,h", "produce help message")
            ("pargs", po::value<stringvec>()->multitoken(), "positional arguments");
        //                          and here     ^^^^^^^^^^^^^
    
        po::command_line_parser clparser(argc, argv);
        clparser.extra_style_parser(end_of_opts_parser);
    
        po::variables_map vm;
        po::store(clparser.options(desc).run(), vm);
        po::notify(vm);
    
        bool const help = !vm["help"].empty();
        std::cout << "help = " << help << " - ";
    
        // in addition, you don't need to use a separate vector of strings:    
        stringvec const& pargs = vm["pargs"].as<stringvec>();
        std::copy(pargs.begin(), pargs.end(),
            std::ostream_iterator<std::string>(std::cout, ","));
    
        return 0;
    }
    

    当使用-h -- foo bar baz 运行时,它会打印help = 1 - foo,bar,baz,

    【讨论】:

    • 我试过你说的,没有成功。请参阅我对代码问题的编辑。
    • @uckelman 我又查看了 Boost 代码,但您遗漏了两件事:opt.value.push_back(*i)po::value...-&gt;multitoken()。我会更新我的答案。
    • 谢谢!我发现它也可以只创建一个 po::option 和 push_back() 所有剩余的值,而不是每次循环创建一个 po::option。那样可能会更有效率。
    猜你喜欢
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-25
    • 2011-12-29
    • 2016-02-22
    相关资源
    最近更新 更多