【问题标题】:How to implement subcommands using Boost.Program_options?如何使用 Boost.Program_options 实现子命令?
【发布时间】:2013-03-21 07:12:27
【问题描述】:

我想在我的程序中实现子命令。我还需要能够为不同的子命令提供不同的参数选项。使用 Boost.Program_options 执行此操作的最佳方法是什么?

子命令用于 svn、git 和 apt-get 等程序。

例如,在 GIT 中,一些可用的子命令是:

git status  
git push  
git add  
git pull  

我的问题和这个人的基本一样:http://boost.2283326.n4.nabble.com/subcommands-with-program-options-like-svn-command-td2585537.html

【问题讨论】:

    标签: c++ boost-program-options


    【解决方案1】:

    如果我对问题的理解正确,您想解析以下形式的命令行选项:

    [--generic-option ...] cmd [--cmd-specific-option ... ] 
    

    这是我的示例解决方案。为清楚起见,我将省略任何验证代码,但希望您能看到如何相当简单地添加它。

    在本例中,我们有“ls”子命令,可能还有其他命令。每个子命令都有一些特定选项,此外还有通用选项。因此,让我们从解析通用选项和命令名称开始。

    po::options_description global("Global options");
    global.add_options()
        ("debug", "Turn on debug output")
        ("command", po::value<std::string>(), "command to execute")
        ("subargs", po::value<std::vector<std::string> >(), "Arguments for command");
    
    po::positional_options_description pos;
    pos.add("command", 1).
        add("subargs", -1);
    
    po::variables_map vm;
    
    po::parsed_options parsed = po::command_line_parser(argc, argv).
        options(global).
        positional(pos).
        allow_unregistered().
        run();
    
    po::store(parsed, vm);
    

    请注意,我们为命令名称创建了一个位置选项,并为命令选项创建了多个位置选项。

    现在我们在相关的命令名称上进行分支并重新解析。我们现在以字符串数组的形式传入无法识别的选项,而不是传入原始的 argcargvcollect_unrecognized 函数可以提供这一点 - 我们所要做的就是删除(位置)命令名称并使用相关的 options_description 重新解析。

    std::string cmd = vm["command"].as<std::string>();
    if (cmd == "ls")
    {
        // ls command has the following options:
        po::options_description ls_desc("ls options");
        ls_desc.add_options()
            ("hidden", "Show hidden files")
            ("path", po::value<std::string>(), "Path to list");
    
        // Collect all the unrecognized options from the first pass. This will include the
        // (positional) command name, so we need to erase that.
        std::vector<std::string> opts = po::collect_unrecognized(parsed.options, po::include_positional);
        opts.erase(opts.begin());
    
        // Parse again...
        po::store(po::command_line_parser(opts).options(ls_desc).run(), vm);
    

    请注意,我们对特定于命令的选项和通用选项使用了相同的 variables_map。从中我们可以执行相关的操作。

    这里的代码片段取自包含一些单元测试的可编译源文件。您可以在 gist here 上找到它。请随时下载并使用它。

    【讨论】:

    • 优秀的答案,有完整的例子来引导。谢谢! --DD
    • 答案中应该明确说明这需要'allow_unregistered()`,这抵消了选项解析库的一大优势。
    • 不确定它可以说得清楚多少——它就在代码 sn-p 和文本中。但是我不认为allow_unregistered()的使用会导致Boost.Program_Options的使用无效(更不用说一般的选项解析库了!),主要是因为无法识别的选项是在没有使用allow_unregistered()的情况下单独解析的。如果allow_unregistered() 用于第二次解析,那么您可能有一点。
    • 公平点,因此您仍然会收到有关无法识别选项的警告。也就是说,po::notify(vm) 不是失踪了吗? OTOH,据我所知,当前代码不需要它。
    【解决方案2】:

    您可以使用 位置选项 将子命令名称从命令行中移除 - 请参阅 this tutorial

    似乎没有对子命令的任何内置支持 - 您需要在顶级解析器上设置 allow_unregistered 选项,找到命令名称,然后通过第二个解析器运行它以获取任何子命令特定的选项。

    【讨论】:

    • 我无法让这个解决方案发挥作用。特别是,Boost 似乎不想让任何东西出现在 位置选项之后。因此,即使使用 allow_unregistered,boost 也会抱怨位置选项太多(即“在命令行上指定了太多位置选项”),即使这些是应该由子解析的非位置选项命令。
    • 死链接:(你能提供另一个或更好的复制粘贴内容吗?
    猜你喜欢
    • 2016-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多