使用 boost::program_options 接受负双精度数

发布于 2024-10-01 12:04:42 字数 1381 浏览 0 评论 0原文

我需要能够让 boost::program_options 解析双精度数组 在命令行上传递的。对于正双打来说,这没有问题, 当然(在 add_options 中使用 multitoken 和 std::vector ),但是对于 消极的,我知道这些都是模棱两可的论点。

以下是我想要了解的内容的演示:

mycommand --extent -1.0 -2.0 -3.0 1.0 2.0 3.0 --some-other-argument somevalue

extent 由具有至少一个构造函数的 Bounds 类支持 它接受六个单独的 T 参数(在本例中为 - double)。

template <typename T>
class Bounds
{
public:
    typedef T value_type;
    typedef typename std::vector< Range<T> >::size_type size_type;

    typedef typename std::vector< Range<T> > Ranges;

    Bounds( T minx, T miny, T minz, 
            T maxx, T maxy, T maxz)
    {
        // fill Ranges vector
    }

private:
    Ranges ranges;
};

我还必须提供什么来支持在 Bounds 类中使用 add_options ? ID 喜欢做类似的事情。可能的?

namespace po = boost::program_options;
po::options_description options("options");

options.add_options()
    ("extent,e", po::value< Bounds< double > >(), "Extent to clip points to")

po::variables_map vm;
po::store(po::command_line_parser(argc, argv).
  options(options).positional(p).run(), vm);

po::notify(vm);

if (vm.count("extent")) 
{
    Bounds<double> bounds = vm["extent"].as< Bounds<double> >();
    // do other stuff
}

I need to be able to have boost::program_options parse an array of doubles
that are passed on a command line. For positive doubles, this is no problem,
of course (use multitoken with std::vector<double> in add_options), but for
negative ones, I know that these are ambiguous arguments.

Here is a demonstration of what I would like to take in:

mycommand --extent -1.0 -2.0 -3.0 1.0 2.0 3.0 --some-other-argument somevalue

extent is to be backed by a Bounds class with at least one constructor
that takes in six individual T arguments (in this case -- double).

template <typename T>
class Bounds
{
public:
    typedef T value_type;
    typedef typename std::vector< Range<T> >::size_type size_type;

    typedef typename std::vector< Range<T> > Ranges;

    Bounds( T minx, T miny, T minz, 
            T maxx, T maxy, T maxz)
    {
        // fill Ranges vector
    }

private:
    Ranges ranges;
};

What else must I supply to support using add_options take in the Bounds class? I'd
like to do something similar to this. Possible?

namespace po = boost::program_options;
po::options_description options("options");

options.add_options()
    ("extent,e", po::value< Bounds< double > >(), "Extent to clip points to")

po::variables_map vm;
po::store(po::command_line_parser(argc, argv).
  options(options).positional(p).run(), vm);

po::notify(vm);

if (vm.count("extent")) 
{
    Bounds<double> bounds = vm["extent"].as< Bounds<double> >();
    // do other stuff
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

往事风中埋 2024-10-08 12:04:42

诀窍是强制 boost 将所有数字分类为位置值(不要与 positional_options_description 混淆)。这样做的方法是定义一个 style_parser 并将其作为 extra_style_parser 提供给 command_line_parser

    #include <boost/program_options/option.hpp>
    #include <boost/lexical_cast/try_lexical_convert.hpp>
    #include <boost/program_options/value_semantic.hpp>

    using po = boost::program_options;

    std::vector<po::option> ignore_numbers(std::vector<std::string>& args)
    {
        std::vector<po::option> result;
        int pos = 0;
        while(!args.empty()) {
            const auto& arg = args[0];
            double num;
            if(boost::conversion::try_lexical_convert(arg, num)) {
                result.push_back(po::option());
                po::option& opt = result.back();

                opt.position_key = pos++;
                opt.value.push_back(arg);
                opt.original_tokens.push_back(arg);

                args.erase(args.begin());
            } else {
                break;
            }
        }

        return result;
    }

一旦获得它,就可以这样使用它:

    po::store(po::command_line_parser(argc, argv)
        .extra_style_parser(&po::ignore_numbers)
        .options(commands)
        .run(), vm);

您现在可以在但是

,仍然存在一个问题,没有办法限制每个参数所采用的标记数量,如果您使用位置参数,这可能会出现问题,例如,这样的方法不起作用:

    foo --coords 1 2 3 4 bar.baz

为了解决这个问题,我们需要添加一种方法来强制参数需要的标记数量:

    template<class T, class charT = char>
    class bounded_typed_value : public po::typed_value<T, charT>
    {
    public:
        bounded_typed_value(T* store_to)
            : typed_value<T, charT>(store_to), m_min(-1), m_max(-1) {}

        unsigned min_tokens() const {
            if(m_min < 0) {
                return po::typed_value<T, charT>::min_tokens();
            } else {
                return (unsigned)m_min;
            }
        }

        unsigned max_tokens() const {
            if(m_max < 0) {
                return po::typed_value<T, charT>::max_tokens();
            } else {
                return (unsigned)m_max;
            }
        }

        bounded_typed_value* min_tokens(unsigned min_tokens)
        {
            if(min_tokens > 1) {
                po::typed_value<T, charT>::multitoken();
            }

            m_min = min_tokens;

            return this;
        }

        bounded_typed_value* max_tokens(unsigned max_tokens)
        {
            if(max_tokens > 1) {
                po::typed_value<T, charT>::multitoken();
            }

            m_max = max_tokens;

            return this;
        }

        bounded_typed_value* fixed_tokens(unsigned num_tokens)
        {
            if(num_tokens > 1) {
                po::typed_value<T, charT>::multitoken();
            }

            m_min = num_tokens;
            m_max = num_tokens;

            return this;
        }

    private:
        int m_min;
        int m_max;
    };


    template<class T, class charT = char>
    bounded_typed_value<T, charT>*
    bounded_value()
    {
        return new bounded_typed_value<T, charT>(0);
    }

您现在可以将它们放在一起,如下所示:

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

    boost::program_options::options_description desc;
    desc.add_options()
        ("coords,c", boost::program_options::bounded_value<std::vector<double>>()->fixed_tokens(4), "Bounding box");

    po::store(po::command_line_parser(argc, argv)
        .extra_style_parser(&po::ignore_numbers)
        .positional(p)
        .options(commands)
        .run(), vm);

The trick is to force boost to classify all numbers as positional values (not to be confused with positional_options_description. The way you do that is define a style_parser and give it to the command_line_parser as an extra_style_parser:

    #include <boost/program_options/option.hpp>
    #include <boost/lexical_cast/try_lexical_convert.hpp>
    #include <boost/program_options/value_semantic.hpp>

    using po = boost::program_options;

    std::vector<po::option> ignore_numbers(std::vector<std::string>& args)
    {
        std::vector<po::option> result;
        int pos = 0;
        while(!args.empty()) {
            const auto& arg = args[0];
            double num;
            if(boost::conversion::try_lexical_convert(arg, num)) {
                result.push_back(po::option());
                po::option& opt = result.back();

                opt.position_key = pos++;
                opt.value.push_back(arg);
                opt.original_tokens.push_back(arg);

                args.erase(args.begin());
            } else {
                break;
            }
        }

        return result;
    }

Once you have it, this is how you use it:

    po::store(po::command_line_parser(argc, argv)
        .extra_style_parser(&po::ignore_numbers)
        .options(commands)
        .run(), vm);

You can now use negative numbers and short command line arguments at the same time.

However, there's still a problem, there's no way to restrict the number of tokens each argument takes, which can be problematic if you use positional arguments. For example, something like this won't work:

    foo --coords 1 2 3 4 bar.baz

In order to fix this, we'll need to add a way to force the number of tokens an argument requires:

    template<class T, class charT = char>
    class bounded_typed_value : public po::typed_value<T, charT>
    {
    public:
        bounded_typed_value(T* store_to)
            : typed_value<T, charT>(store_to), m_min(-1), m_max(-1) {}

        unsigned min_tokens() const {
            if(m_min < 0) {
                return po::typed_value<T, charT>::min_tokens();
            } else {
                return (unsigned)m_min;
            }
        }

        unsigned max_tokens() const {
            if(m_max < 0) {
                return po::typed_value<T, charT>::max_tokens();
            } else {
                return (unsigned)m_max;
            }
        }

        bounded_typed_value* min_tokens(unsigned min_tokens)
        {
            if(min_tokens > 1) {
                po::typed_value<T, charT>::multitoken();
            }

            m_min = min_tokens;

            return this;
        }

        bounded_typed_value* max_tokens(unsigned max_tokens)
        {
            if(max_tokens > 1) {
                po::typed_value<T, charT>::multitoken();
            }

            m_max = max_tokens;

            return this;
        }

        bounded_typed_value* fixed_tokens(unsigned num_tokens)
        {
            if(num_tokens > 1) {
                po::typed_value<T, charT>::multitoken();
            }

            m_min = num_tokens;
            m_max = num_tokens;

            return this;
        }

    private:
        int m_min;
        int m_max;
    };


    template<class T, class charT = char>
    bounded_typed_value<T, charT>*
    bounded_value()
    {
        return new bounded_typed_value<T, charT>(0);
    }

You can now put it all together like this:

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

    boost::program_options::options_description desc;
    desc.add_options()
        ("coords,c", boost::program_options::bounded_value<std::vector<double>>()->fixed_tokens(4), "Bounding box");

    po::store(po::command_line_parser(argc, argv)
        .extra_style_parser(&po::ignore_numbers)
        .positional(p)
        .options(commands)
        .run(), vm);
空心空情空意 2024-10-08 12:04:42

此处指定的处理负数的方法可能也适合您。

我用简单的解析器解析它

store(command_line_parser(argc, argv).options(commands).run(), vm);

,但解决方案是使用扩展
一:

parse_command_line

The approach to handling negative numbers specified here might also work for you.

I was parsing it by the simple parser

store(command_line_parser(argc, argv).options(commands).run(), vm);

, but solution was to use the extended
one:

parse_command_line
剪不断理还乱 2024-10-08 12:04:42

最简单的方法是将参数用引号引起来:
mycommand --extent '-1.0 -2.0 -3.0 1.0 2.0 3.0' --some-other-argument somevalue

The easy way would be to wrap your parameters in quotes:
mycommand --extent '-1.0 -2.0 -3.0 1.0 2.0 3.0' --some-other-argument somevalue

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文