首页 > 解决方案 > 我正在使用 Boost.Spirit 来使用 boost::tuple 来检索解析器的属性,但没有成功

问题描述

看到这段代码:

                     using qi::int_;
                      using qi::phrase_parse;
                      using qi::_1;
                      using ascii::space;
                      
                      std::string s = "50 43.3 77.44";   
                      first = s.begin();
                      last = s.end();
boost::tuple<boost::optional<double>, boost::optional<int>> to;

bool r = phrase_parse(first, last,
(
double_ || int_
),
space, to);                  // r == true

auto a = to.get<0>(); // this works catches the 50 from the input
auto aa = *a;
auto b = to.get<1>();      // this does not work even though 43 is parsed correctly
auto bb = *b;             // EXCEPTION

我尝试将这种奇怪的行为放在 spirit-general@lists.sourceforge.net 但没有人回答

代码非常小。如果有人尝试编译并运行它,我将不胜感激!

问候,胡安

标签: c++boostboost-spirit

解决方案


qi::double_ | qi::int_解析备选方案,而不是序列

换句话说,我希望您使用qi::double_ | qi::int_来解析variant<double, int>(或兼容)。

相反,用于qi::double_ >> qi::int_解析tuple<double, int>(或兼容)。

现在,double_ || int_这是一个奇怪的序列:它是一个可选/替代序列:https ://www.boost.org/doc/libs/1_74_0/libs/spirit/doc/html/spirit/qi/reference/operator/sequential_or.html 。那么......你期望会发生什么?

目标是什么?

看起来您正在解析以空格分隔的数字。他们有3个。但是你解析成两个元组。用另一种顺序。其中两个分支冲突(任何整数都将解析为双精度数,而双精度数的整数部分可以解析为整数,正如您所熟知的,因为您声称“即使正确解析了 43”)。

我不知道你想要实现什么,因为大多数人永远不会说“43 被正确解析”,因为输入中没有数字 43(这只是 43.3 的一部分)。

让我们演示一下选项

因为很难猜出您在此处实际尝试解析的内容,所以让我们避免选择并仅演示从您的示例开始的所有方法:

住在科利鲁

#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/lexical_cast.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;

std::string const sample = "50 43.3 77.44";

template <typename T>
static std::string debug_string(T const& v, std::string const& sep = "") {
    return boost::lexical_cast<std::string>(v) + sep;
}

template <typename... T>
static auto debug_string(std::tuple<T...> const& v, std::string const& sep = "") {
    std::ostringstream oss;
    oss << "( ";
    auto out = [&oss](auto const&... elements) {
        (oss << ... << debug_string(elements, " ")) << ")";
    };

    std::apply(out, v);
    return oss.str() + sep;
}

template <typename Parser, typename... Attrs>
void demo_parse(Parser const& p, Attrs... attrs) {
    auto first = sample.begin();
    auto last = sample.end();

    if (qi::phrase_parse(first, last, p, qi::space, attrs...)) {
        ((std::cout << "Parsed: ") << ... << debug_string(attrs, " ")) << "\n";
    } else {
        std::cout << "Parse failed\n";
    }

    if (first != last) {
        std::cout << "Remaining input: " << std::quoted(std::string(first, last)) << "\n";
    }
    std::cout << "------------------------\n";
}

int main() {
    std::cout << sample << "\n========================\n";
    
    //// as a sequence
    std::cout << "As a sequence" << "\n========================\n";
    // note that int vs double input can be confusing:
    auto parse_sequence = [](auto p) {
        double d = 0; int i = 0;
        demo_parse(p, d, i);
        demo_parse(p, i, d);
        demo_parse(p, std::tuple<double, int>{});
        demo_parse(p, std::tuple<int, double>{});
        demo_parse(p, std::tuple<boost::optional<double>, boost::optional<int> >{});
        demo_parse(p, std::tuple<boost::optional<int>, boost::optional<double> >{});
    };
    parse_sequence(qi::int_ >> qi::double_);
    parse_sequence(qi::double_ >> qi::int_);

    // as an alternative
    std::cout << "As alternatives" << "\n========================\n";
    // - keeping in mind the confusion between double_ and int_, using
    //   strict_real_policies
    qi::real_parser<double, qi::strict_real_policies<double> > sdouble_;
    demo_parse(sdouble_ | qi::int_, boost::variant<int, double>{});
    demo_parse(sdouble_ | qi::int_, boost::variant<double, int>{});
    demo_parse(sdouble_ | qi::int_, boost::optional<boost::variant<int, double>>{});

    // as alternative sequences
    std::cout << "As alternative sequences" << "\n========================\n";
    // - I don't know why this would be useful, and keep in mind the
    //   strict_real_policies again
    demo_parse(sdouble_ || qi::int_, std::tuple<int, double>{});
    demo_parse(sdouble_ || qi::int_, std::tuple<double, int>{});
    demo_parse(sdouble_ || qi::int_, std::tuple<boost::optional<double>, boost::optional<int> >{});

    std::cout << "Alternatives into variant type" << "\n========================\n";
    demo_parse(sdouble_ || qi::int_, boost::variant<int, double>{});
    demo_parse(sdouble_ || qi::int_, boost::variant<double, int>{});
    demo_parse(sdouble_ || qi::int_, boost::optional<boost::variant<double, int>> {});
}
    

印刷

50 43.3 77.44
========================
As a sequence
========================
Parsed: 50 43 
Remaining input: "77.44"
------------------------
Parsed: 50 43.299999999999997 
Remaining input: "77.44"
------------------------
Parsed: ( 50 43 ) 
Remaining input: "77.44"
------------------------
Parsed: ( 50 43.299999999999997 ) 
Remaining input: "77.44"
------------------------
Parsed: (  50  43 ) 
Remaining input: "77.44"
------------------------
Parsed: (  50  43.3 ) 
Remaining input: "77.44"
------------------------
Parsed: 50 43 
Remaining input: ".3 77.44"
------------------------
Parsed: 50 43 
Remaining input: ".3 77.44"
------------------------
Parsed: ( 50 43 ) 
Remaining input: ".3 77.44"
------------------------
Parsed: ( 50 43 ) 
Remaining input: ".3 77.44"
------------------------
Parsed: (  50  43 ) 
Remaining input: ".3 77.44"
------------------------
Parsed: (  50  43 ) 
Remaining input: ".3 77.44"
------------------------
As alternatives
========================
Parsed: 50 
Remaining input: "43.3 77.44"
------------------------
Parsed: 50 
Remaining input: "43.3 77.44"
------------------------
Parsed:  50 
Remaining input: "43.3 77.44"
------------------------
As alternative sequences
========================
Parsed: ( 0 50 ) 
Remaining input: "43.3 77.44"
------------------------
Parsed: ( 0 50 ) 
Remaining input: "43.3 77.44"
------------------------
Parsed: ( --  50 ) 
Remaining input: "43.3 77.44"
------------------------
Alternatives into variant type
========================
Parsed: 0 
Remaining input: "43.3 77.44"
------------------------
Parsed: 0 
Remaining input: "43.3 77.44"
------------------------
Parsed: -- 
Remaining input: "43.3 77.44"
------------------------

或者也许:理智选项#1

这可能是压倒性的,所以这就是我认为你可能一直在追求的:只需解析一个 int-or-double 序列:

住在科利鲁

// #define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;

std::string const sample = "50 43.3 77.44";
using It = std::string::const_iterator;

int main() {
    std::cout << sample << "\n========================\n";
    
    using Value = boost::variant<double, int>;
    using Values = std::vector<Value>;
    qi::real_parser<double, qi::strict_real_policies<double> > sdouble_;
    qi::rule<It, Value()> double_or_int = sdouble_ | qi::int_;
    BOOST_SPIRIT_DEBUG_NODE(double_or_int);

    auto first = sample.begin();
    auto last = sample.end();

    Values values;
    if (qi::phrase_parse(first, last, *double_or_int, qi::space, values)) {
        std::cout << "Parsed:";
        for (auto v : values)
            std::cout << " " << v;
        std::cout << "\n";
    } else {
        std::cout << "Parse failed\n";
    }

    if (first != last) {
        std::cout << "Remaining input: " << std::quoted(std::string(first, last)) << "\n";
    }
}

印刷

50 43.3 77.44
========================
Parsed: 50 43.3 77.44

理智(?)选项#2

也许线索在于“43被正确解析”的说法的奇怪之处。如果 43 被“正确”解析,则表示句点(“.”)不是小数分隔符。那么也许您想解析“1 2 . 3 4 . 5 6 . 7 8” 样式输入?

住在科利鲁

#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;

std::string const sample = "50 43.3 77.44";
using It = std::string::const_iterator;

int main() {
    std::cout << sample << "\n========================\n";

    auto first = sample.begin();
    auto last = sample.end();

    using Pair = std::pair<int, int>;
    std::vector<Pair> values;
    if (qi::phrase_parse(first, last, (qi::int_ >> qi::int_) % '.', qi::space, values)) {
        std::cout << "Parsed:";
        for (auto [a,b] : values)
            std::cout << " (" << a << " " << b << ")";
        std::cout << "\n";
    } else {
        std::cout << "Parse failed\n";
    }

    if (first != last) {
        std::cout << "Remaining input: " << std::quoted(std::string(first, last)) << "\n";
    }
}

印刷

50 43.3 77.44
========================
Parsed: (50 43) (3 77)
Remaining input: ".44"

推荐阅读