【问题标题】:How to add a description to boost::program_options' positional options?如何为 boost::program_options 的位置选项添加描述?
【发布时间】:2016-10-08 12:01:37
【问题描述】:

我想使用 boost_program_options 创建一个位置列表程序选项,该选项不允许命名程序选项(如 --files)。

我有以下sn-p的代码:

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

namespace po = boost::program_options;

int main(int argc, const char* argv[]) {
  po::options_description desc("Allowed options");
  desc.add_options()("help", "produce help message")
  ( "files", po::value<std::vector<std::string>>()->required(), "list of files");

  po::positional_options_description pos;
  pos.add("files", -1);

  po::variables_map vm;
  try {
    po::store(po::command_line_parser(argc, argv).options(desc).positional(pos).run(), vm);
    po::notify(vm);
  } catch(const po::error& e) {
    std::cerr << "Couldn't parse command line arguments properly:\n";
    std::cerr << e.what() << '\n' << '\n';
    std::cerr << desc << '\n';
    return 1;
  }

  if(vm.count("help") || !vm.count("files")) {
    std::cout << desc << "\n";
    return 1;
  }
}

问题是我可以将文件列表读取为位置参数列表,如下所示:

./a.out file1 file2 file3

但不幸的是,我也喜欢这样(我想禁用它)

./a.out --files file1 file2 file3

问题还在于产生的帮助:

./a.out
Couldn't parse command line arguments properly:
the option '--files' is required but missing

Allowed options:
  --help                produce help message
  --files arg           list of files

所以我想要的场景会更像(os 相似):

./a.out
Couldn't parse command line arguments properly:
[FILES ...] is required but missing

Allowed options:
  --help                produce help message
  --optionx             some random option used in future
  [FILE ...]            list of files

desc.add_option()(...) 中删除files 选项后,它停止工作,所以我相信我需要它。

【问题讨论】:

  • 为什么需要移除使用命名参数指定输入文件的能力?它对任何东西都没有害处,那么为什么要竭尽全力禁用它呢?
  • @DanMašek 我相信用户并不太清楚,因为这两种方式都可以成为有效的输入策略(我特别想要一种并且有帮助来支持它)
  • 好的。那里有在我看来侵入性最小的解决方案。它仍然允许位置选项分散在整个参数列表中,但您可以轻松添加更多验证以对其进行更多限制。
  • 顺便说一句,我认为您应该更改标题以更好地反映您的问题的主体。这是一个艰难的问题,但是像“使用位置选项并禁止其显式变体”之类的东西可能会更好。

标签: c++ c++11 boost boost-program-options


【解决方案1】:

关于标题中提出的问题,“如何为 boost::program_options 的位置选项添加描述?”,库中为此提供了no functionality。你需要自己处理这部分。

至于问题的主体......这是可能的,但方式有点迂回。

位置选项将每个位置映射到一个名称,并且名称需要存在。从我在代码(cmdline.cpp)中可以看出,unregistered 标志不会为位置参数设置。 [1], [2]

所以,要为所欲为,我们可以执行以下操作:

  • 在帮助中隐藏--files 选项。您需要自己为位置选项显示适当的帮助,但这与以前没有什么不同。
  • variables_map 中添加我们自己的解析和存储解析选项之间的验证。

从帮助中隐藏--files

这里我们利用了我们可以使用add(...) 成员函数创建复合options_description 的事实:

po::options_description desc_1;
// ...
po::options_description desc_2;
// ...
po::options_description desc_composite;
desc_composite.add(desc_1).add(desc_2);

因此,我们可以将files 选项放入隐藏的options_description,并创建一个仅用于解析阶段的组合。 (见下面的代码)

防止显式--files

我们需要在解析和将它们存储到variables_map之间截取选项列表。

command_line_parserrun() 方法返回一个basic_parsed_options 的实例,其成员options 持有basic_options 的向量。每个解析的参数都有一个元素,任何位置选项都从0开始枚举,任何非位置选项都有位置-1。当我们将--files 视为显式(非位置)参数时,我们可以使用它来执行我们自己的验证并引发错误。

示例源代码

See on Coliru

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

namespace po = boost::program_options;

int main(int argc, const char* argv[])
{
    std::vector<std::string> file_names;

    po::options_description desc("Allowed options");
    desc.add_options()
        ("help", "produce help message")
        ("test", "test option");

    std::string const FILES_KEY("files");

    // Hide the `files` options in a separate description
    po::options_description desc_hidden("Hidden options");
    desc_hidden.add_options()
        (FILES_KEY.c_str(), po::value(&file_names)->required(), "list of files");

    // This description is used for parsing and validation
    po::options_description cmdline_options;
    cmdline_options.add(desc).add(desc_hidden);

    // And this one to display help
    po::options_description visible_options;
    visible_options.add(desc);

    po::positional_options_description pos;
    pos.add(FILES_KEY.c_str(), -1);

    po::variables_map vm;
    try {
        // Only parse the options, so we can catch the explicit `--files`
        auto parsed = po::command_line_parser(argc, argv)
            .options(cmdline_options)
            .positional(pos)
            .run();

        // Make sure there were no non-positional `files` options
        for (auto const& opt : parsed.options) {
            if ((opt.position_key == -1) && (opt.string_key == FILES_KEY)) {
                throw po::unknown_option(FILES_KEY);
            }
        }

        po::store(parsed, vm);
        po::notify(vm);
    } catch(const po::error& e) {
        std::cerr << "Couldn't parse command line arguments properly:\n";
        std::cerr << e.what() << '\n' << '\n';
        std::cerr << visible_options << '\n';
        return 1;
    }

    if (vm.count("help") || !vm.count("files")) {
        std::cout << desc << "\n";
        return 1;
    }

    if (!file_names.empty()) {
        std::cout << "Files: \n";
        for (auto const& file_name : file_names) {
            std::cout << " * " << file_name << "\n";
        }
    }
}

测试输出

有效选项:

>example a b c --test d e
Files:
 * a
 * b
 * c
 * d
 * e

无效选项:

>example a b c --files d e
Couldn't parse command line arguments properly:
unrecognised option 'files'


Allowed options:
  --help                 produce help message
  --test                 test option

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多