如何在使用 boost 进行 QI 解析期间对数字进行舍入?

How to round a number during QI parsing with boost?

使用简单的 Boost qi 语法,我怎样才能让它四舍五入我的数字?

这是解析器:

factor =
            float_                          [_val = _1]
            |   ('-' >> factor              [_val = -_1])
            |   ('+' >> factor              [_val = _1])
            ;

这可以解析一个浮点数,也可以是负数。

我想舍入浮点数,所以我会在语法中添加这样的内容:

|   ('~' >> factor              [_val = round(_1)])

但这会导致编译时错误:

no type named ‘__type’ in ‘struct __gnu_cxx::__enable_if<false, double>’

这个错误对我来说不是太有用,你能帮忙吗?我希望能够舍入一个数字,即:

~1.8 -> 2
~1.2 -> 1

注意:我正在解析 phrase_parse

语义动作需要 Phoenix Actor,这是延迟函数。

选项:

  1. 适配宏https://www.boost.org/doc/libs/1_73_0/libs/phoenix/doc/html/phoenix/modules/function/adapting_functions.html
  2. phoenix::function<>
  3. phoenix::bind
  4. 自己写,详见https://www.boost.org/doc/libs/1_68_0/libs/spirit/doc/html/spirit/qi/tutorials/semantic_actions.html#spirit.qi.tutorials.semantic_actions.examples_of_semantic_actions

简化测试台

只是解析一个数字:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;

int main() {
    std::string s = "1.75";

    double v;
    if (qi::parse(begin(s), end(s), qi::double_, v)) {
        std::cout << "Parsed: " << v << "\n";
    }
}

打印 Live On Coliru:

Parsed: 1.75

适应

使用宏:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <cmath>
namespace qi = boost::spirit::qi;

int main() {
    std::string s = "1.75";

    double v;
    if (qi::parse(begin(s), end(s), qi::double_, v)) {
        std::cout << "Parsed: " << v << "\n";
        std::cout << "Rounded: " << round(v) << "\n";
    }
}

打印 Live On Coliru:

Parsed: 2

function<>

您可以在此处对签名进行硬编码:

boost::phoenix::function<double(*)(double)> round_(::round);

然而真正的力量来自于多态可调用对象:

struct round_f {
    template <typename T> auto operator()(T const& v) const {
        using std::round; // activate ADL
        return round(v);
    }
};
boost::phoenix::function<round_f> round_{};

现在您可以在任何具有兼容的自由函数重载 round 重载的类型上使用 round_ actor。如果明天您决定解析 long doublefloatboost::multiprecision::cpp_dec_float.

,这会很方便

看到了Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <cmath>
namespace qi = boost::spirit::qi;

struct round_f {
    template <typename T> auto operator()(T const& v) const {
        using std::round; // activate ADL
        return round(v);
    }
};
boost::phoenix::function<round_f> round_{};

int main() {
    std::string s = "1.75";

    double v;
    using namespace qi::labels;
    if (qi::parse(begin(s), end(s), qi::double_ [ _val = round_(_1) ], v)) {
        std::cout << "Parsed: " << v << "\n";
    }
}

版画

Parsed: 2

使用phoenix::bind

作为较低级别的构建块,您可以绑定未包装的可调用对象:

if (qi::parse(begin(s), end(s), qi::double_ 
            [ _val = phoenix::bind(round_f{}, _1) ], v))
{
    std::cout << "Parsed: " << v << "\n";
}

如果不介意丑的话:

if (qi::parse(begin(s), end(s), qi::double_ 
    [ _val = phoenix::bind(static_cast<double(&)(double)>(std::round), _1) ], v))
{
    std::cout << "Parsed: " << v << "\n";
}

同时查看 Live On Coliru