c++ 使用boost::program_options指定多个标志

tkclm6bt  于 2023-04-01  发布在  其他
关注(0)|答案(2)|浏览(147)

我想使用boost::program_options来指定所需的详细程度,这是很常见的。

./test -v # verbosity = 1 
./test -vvv # verbosity = 3 
./test -v blah blah -v # verbosity = 2

我知道如何处理需要一个值的选项的多次出现,但我想要的是一个开关的多次出现。

desc.add_options()
   ("verbosity,v", bool_switch(), "Increase verbosity");

但如果提供了多个-v选项,则会失败,并出现multiple_occurrence异常。
多个boolean选项可以用类似

desc.add_options()
   ("verbose,v", value<std::vector<int> >(), "Increase verbosity");

但这需要为每个选项指定一个值,例如

./test -v 1 -v 1 -v 1
ni65a41a

ni65a41a1#

我也遇到了这个问题,并提出了以下建议。希望它能对某些人有用,即使对于最初的海报来说已经太晚了。

#include "StdAfx.h"
#include <boost\program_options.hpp>
#include <iostream>

namespace po = boost::program_options;

class CountValue : public po::typed_value<std::size_t>
{
public:
    CountValue():
        CountValue(nullptr)
    {
    }

    CountValue(std::size_t* store):
        po::typed_value<std::size_t>(store)
    {
        // Ensure that no tokens may be passed as a value.
        default_value(0);
        zero_tokens();
    }

    virtual ~CountValue()
    {
    }

    virtual void xparse(boost::any& store, const std::vector<std::string>& /*tokens*/) const
    {
        // Replace the stored value with the access count.
        store = boost::any(++count_);
    }

private:
    mutable std::size_t count_{ 0 };
};

int main(int argc, char** argv)
{
    // Define the command line options (we create a new CountValue
    // instance, on the understanding that it will be deleted for us
    // when the options description instance goes out of scope).
    po::options_description options("Options");
    std::size_t verbose = 0;
    options.add_options()
        ("verbose,v", new CountValue(&verbose), "Increase verbosity");

    // Parse the command line options.
    po::command_line_parser parser(argc, argv);
    po::variables_map variables;
    po::store(parser.options(options).run(), variables);
    po::notify(variables);

    // Show the verbosity level.
    std::cout << "Verbosity " << verbose << "\n";
}

这是用C++20和Boost 1.77编译的。下面是它的运行情况:

$ test
Verbosity 0

$ test -v
Verbosity 1

$ test -vvv
Verbosity 3

$ test -v blah blah -v
Verbosity 2
jfewjypa

jfewjypa2#

Huw Walters的答案对我来说非常有用。我在上面做了一些扩展,允许通过一个数字参数显式地设置verbosity级别。所以所有这些调用都会将verbosity设置为4:

app -v -v -v -v
app -v4
app -vvvv
app -vvv -v
app -v3 -v

不幸的是,该解决方案的缺点是在-v之后混合其他选项不起作用。因此,即使x也是法律的选项,app -vx也会产生错误。
下面是代码。要使用它,请提供:(“verbose,v”,new po_CountAndAddValue(&verbose),“增加详细度”);作为运算符到add_options()

class po_CountAndAddValue : public po::typed_value<std::size_t>
{
public:
    po_CountAndAddValue(std::size_t* store)
      : po::typed_value<std::size_t>(store)
    {
        default_value(0);
    }
    po_CountAndAddValue() : po_CountAndAddValue(nullptr) {}
    ~po_CountAndAddValue() override {}

    // Allow zero or one tokens:
    unsigned min_tokens() const override { return 0; }
    unsigned max_tokens() const override { return 1; }

    void xparse(boost::any& store,
        const std::vector<std::string>& tokens) const override
    {
    if(tokens.empty()) {
        count_++;
    } else {
        const std::string& tok = tokens[0];
        if(std::all_of(tok.begin(), tok.end(), [](char c){return c=='v';})){
        count_ += 1 + tok.size();
        } else {
        // Add explicitely specified verbosity count:
        count_ += boost::lexical_cast<int>(tok);
        }
    }

        // Replace the stored value with the updated access count:
        store = boost::any(count_);
    }

private:
    mutable std::size_t count_{ 0 };
};

如果您不希望混合使用带有和不带有数字参数的verbosity选项,则可以为这种情况添加错误报告,如下所示:

class po_CountOrSetValue : public po::typed_value<std::size_t>
{
public:
    po_CountOrSetValue(std::size_t* store)
      : po::typed_value<std::size_t>(store)
    {
        default_value(0);
    }
    po_CountOrSetValue() : po_CountOrSetValue(nullptr) {}
    ~po_CountOrSetValue() override {}

    // Allow zero or one tokens:
    unsigned min_tokens() const override { return 0; }
    unsigned max_tokens() const override { return 1; }

    class mixed_types_error : public po::error_with_option_name {
    public:
        mixed_types_error()
         : po::error_with_option_name("option '%canonical_option%' wrongly used both with and without value"){}
        ~mixed_types_error() throw() {}
    };

    void xparse(boost::any& store,
        const std::vector<std::string>& tokens) const override
    {
    if(tokens.empty() ||
       std::all_of(tokens[0].begin(), tokens[0].end(),
               [](char c){return c=='v';}))
    {
        if(set_) {
        throw mixed_types_error();
        }
        count_ += tokens.empty() ? 1 : 1 + tokens[0].size();
    } else {
        if(std::exchange(set_, true)) {
        throw po::multiple_occurrences();
        }
        if(count_ != 0) {
        throw mixed_types_error();
        }
        count_ = boost::lexical_cast<int>(tokens[0]);
    }

        // Replace the stored value with the updated count:
        store = boost::any(count_);
    }

private:
    mutable std::size_t count_{ 0 };
    mutable bool set_{ false };
};

所有这些说明符在第一个版本中都可以将详细程度设置为4,但在第二个版本中失败了:

app -v3 -v
app -v3 -v1
app -vv -v2

所有这些都是用C++17和Boost 1.74测试的。

相关问题