首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Boost operator运算符并不能填充所有属性值

Boost operator运算符并不能填充所有属性值
EN

Stack Overflow用户
提问于 2021-10-07 11:28:43
回答 1查看 67关注 0票数 2

我读了一个文件中的实数,用的是精神气。我尝试实现条件解析器,其中输入取决于行中的第一个字符。

代码语言:javascript
复制
#include <iostream>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/spirit/include/qi.hpp>
using namespace std;
namespace qi = boost::spirit::qi;


struct  MyStruct {

   double r1, r2, r3, r4;
   double r5, r6, r7, r8;
};

BOOST_FUSION_ADAPT_STRUCT(
   MyStruct,

   (double, r1), (double, r2), (double, r3), (double, r4),
   (double, r5), (double, r6), (double, r7), (double, r8)
);

int main(int argc, wchar_t* argv[])
{
   string test =
      "A+1.000000000000e+00+2.000000000000e+00+3.000000000000e+00+4.000000000000e+00\r\n"
      "B+5.000000000000e+00+6.000000000000e+00+7.000000000000e+00+8.000000000000e+00\r\n";
   qi::rule<string::const_iterator> CRLF = qi::copy(qi::lit("\r\n"));
   qi::real_parser d19_12;

   MyStruct ms;
   qi::rule<string::const_iterator, MyStruct()> gr =

      qi::lit("A") >> d19_12 >> d19_12 >> d19_12 >> d19_12 >> CRLF
      >> (
         (qi::lit('B') >> d19_12  >> d19_12 >> d19_12 >> d19_12 >> CRLF)
         |
         (qi::lit('C') >> d19_12  >> d19_12 >> d19_12 >> +qi::lit('_') >> qi::attr(0.0) >> CRLF)
         )
      ;
   string::const_iterator f = test.cbegin();
   string::const_iterator e = test.cend();
   bool ret = qi::parse(f, e, gr, ms);

   return ret;
}

如果没有“C”选项,所有东西都可以正常工作,但是添加这个选项会使解析器跳过值,结果是

  • MyStruct r1 1.0000000000000000双r2 2.0000000000000000双r3 3.0000000000000000双r4 4.0000000000000000双r5 5.0000000000000000双r6 -9.2559631349317831e+61双r7 -9.2559631349317831e+61双r8 -9.2559631349317831e+61

预期结果是:

  • MyStruct r1 1.0000000000000000双r2 2.0000000000000000双r3 3.0000000000000000双r4 4.0000000000000000双r5 5.0000000000000000双r6 6.0000000000000000双r7 7.0000000000000000双r8 8.0000000000000000

谢谢

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-10-07 14:17:53

您可以调试规则。因此,简化"A+1+2+3+4\r\nB+5+6+7+8\r\n"的输入并将真正的解析器包装成规则,这就是调试输出:

住在Coliru

代码语言:javascript
复制
<gr>
  <try>A+1+2+3+4\r\nB+5+6+7+8</try>
  <d19_12>
    <try>+1+2+3+4\r\nB+5+6+7+8\r</try>
    <success>+2+3+4\r\nB+5+6+7+8\r\n</success>
    <attributes>[1]</attributes>
  </d19_12>
  <d19_12>
    <try>+2+3+4\r\nB+5+6+7+8\r\n</try>
    <success>+3+4\r\nB+5+6+7+8\r\n</success>
    <attributes>[2]</attributes>
  </d19_12>
  <d19_12>
    <try>+3+4\r\nB+5+6+7+8\r\n</try>
    <success>+4\r\nB+5+6+7+8\r\n</success>
    <attributes>[3]</attributes>
  </d19_12>
  <d19_12>
    <try>+4\r\nB+5+6+7+8\r\n</try>
    <success>\r\nB+5+6+7+8\r\n</success>
    <attributes>[4]</attributes>
  </d19_12>
  <CRLF>
    <try>\r\nB+5+6+7+8\r\n</try>
    <success>B+5+6+7+8\r\n</success>
    <attributes>[]</attributes>
  </CRLF>
  <d19_12>
    <try>+5+6+7+8\r\n</try>
    <success>+6+7+8\r\n</success>
    <attributes>[5]</attributes>
  </d19_12>
  <d19_12>
    <try>+6+7+8\r\n</try>
    <success>+7+8\r\n</success>
    <attributes>[6]</attributes>
  </d19_12>
  <d19_12>
    <try>+7+8\r\n</try>
    <success>+8\r\n</success>
    <attributes>[7]</attributes>
  </d19_12>
  <d19_12>
    <try>+8\r\n</try>
    <success>\r\n</success>
    <attributes>[8]</attributes>
  </d19_12>
  <CRLF>
    <try>\r\n</try>
    <success></success>
    <attributes>[]</attributes>
  </CRLF>
  <success></success>
  <attributes>[[1, 2, 3, 4, 5, 4.27256e+180, 0, 0]]</attributes>
</gr>
Parsed: (1 2 3 4 5 4.27256e+180 0 0)

事实上,它证实了所有的数字都被解析了。为什么属性传播没有做您期望的事情?

我的猜测是,它是属性传播,试图接受比您预期的多一点。问题是AST与规则不直接匹配:规则合成

代码语言:javascript
复制
tup4 := tuple<double, double, double, double>
attribute := tuple<tup4, variant<tup4, tup4> >

在齐版本中,这确实被简化为tuple<tup4, tup4>,但是您的tup8实际上就像一个tup8,这是不一样的。因此,在传播时,规则只是做它认为是最好的选择,即分配第一个tup4。然后:耸耸肩:

修复

最简单的解决方法是使AST与规则相匹配。这可能非常有意义,因为更有可能的是,"A""B"、"C“都有语义意义。

代码语言:javascript
复制
namespace Ast {
    struct A {
        double r1, r2, r3, r4;
    };
    struct BC {
        double r5, r6, r7, r8;
    };
    struct MyStruct {
        A  a;
        BC bc;
    };

    using boost::fusion::operator<<;
} // namespace Ast

调整它们:

代码语言:javascript
复制
BOOST_FUSION_ADAPT_STRUCT(Ast::A, r1, r2, r3, r4)
BOOST_FUSION_ADAPT_STRUCT(Ast::BC, r5, r6, r7, r8)
BOOST_FUSION_ADAPT_STRUCT(Ast::MyStruct, a, bc)

请注意,没有进一步的更改,这只是确认了自动属性传播是一种基于启发式的方法:柯尔鲁Parsed: ((1 0 0 0) (2 0 0 0)) (oops)。

使规则符合这一结构:

代码语言:javascript
复制
qi::rule<It>         CRLF   = "\r\n";
qi::rule<It, double> d19_12 = qi::double_;

qi::rule<It, Ast::A()>  A  = "A" >> d19_12 >> d19_12 >> d19_12 >> d19_12; //
qi::rule<It, Ast::BC()> BC =                                              //
    'B' >> d19_12 >> d19_12 >> d19_12 >> d19_12 |                         //
    'C' >> d19_12 >> d19_12 >> d19_12 >> +qi::lit('_') >> qi::attr(0.0);

qi::rule<It, Ast::MyStruct()> gr = A >> CRLF >> BC >> CRLF;

现在一切都成功了:柯尔鲁

打印

代码语言:javascript
复制
Parsed: ((1 2 3 4) (5 6 7 8))

他山之石

在我看来,很多这些都是XY的问题。一个结构,有8个无特征的数字,可以有不同的含义似乎.不是你真正需要的。

此外,B/C的区别似乎表明你真的想要一个“可选数字”规则:

代码语言:javascript
复制
rule<It>         CRLF   = "\r\n";
rule<It, double()> d19_12 = raw[ //
    double_[_val = _1] |         //
    omit[+char_("_")]            //
][_pass = px::size(_1) == 19];

rule<It, Ast::Tup4()> Tup4 =
    omit[char_("ABC")] >> d19_12 >> d19_12 >> d19_12 >> d19_12;

请注意,“省略[char(”ABC“)]`_直接反映了我的直觉,即您在模型中丢弃语义信息。

现在语法变成了

代码语言:javascript
复制
rule<It, Ast::MyStruct()> gr = Tup4 >> CRLF >> Tup4 >> CRLF;

实际上,它解析了完整的输入:柯尔鲁

代码语言:javascript
复制
Parsed: ((1.0001 2.0002 3.0003 4.0004) (5.0005 6.0006 7.0007 8.0008))

简化!容器

事实上,我怀疑你可能会得到更好的服务,比如:

代码语言:javascript
复制
namespace Ast {
    using Reals = boost::container::static_vector<double, 8>;
} // namespace Ast

有趣的是,容器do享受更灵活的属性传播(使用新的警告)。你可以做一些直截了当的事情,比如:

代码语言:javascript
复制
qi::rule<It, Ast::Reals(char const*)> Line =
    qi::omit[qi::char_(_r1)] >> d19_12 >> d19_12 >> d19_12 >> d19_12;

qi::rule<It, Ast::Reals()> gr = //
    Line(+"A") >> CRLF >> Line(+"BC") >> CRLF;

让我以这样一个活生生的例子结束:在编译器资源管理器上直播¹

代码语言:javascript
复制
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/container/static_vector.hpp>
#include <fmt/ranges.h>
#include <iomanip>
#include <iostream>
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;

namespace Ast {
    using Reals = boost::container::static_vector<double, 8>;
} // namespace Ast

int main()
{
    using It = std::string::const_iterator;
    using namespace qi::labels;

    qi::rule<It>         CRLF   = "\r\n";
    qi::rule<It, double()> d19_12 = qi::raw[ //
        qi::double_[_val = _1] |             //
        qi::omit[+qi::char_("_")]            //
    ][_pass = px::size(_1) == 19];

    qi::rule<It, Ast::Reals(char const*)> Line =
        qi::omit[qi::char_(_r1)] >> d19_12 >> d19_12 >> d19_12 >> d19_12;

    qi::rule<It, Ast::Reals()> gr = //
        Line(+"A") >> CRLF >> Line(+"BC") >> CRLF;

    BOOST_SPIRIT_DEBUG_NODES((gr)(Line)(d19_12)(CRLF))

    for (std::string const test : {
             "A+1.000100000000e+00+2.000200000000e+00+3.000300000000e+00+4.000400000000e+00\r\n"
             "B+5.000500000000e+00+6.000600000000e+00+7.000700000000e+00+8.000800000000e+00\r\n",
             "A+1.000100000000e+00+2.000200000000e+00+3.000300000000e+00+4.000400000000e+00\r\n"
             "C+5.000500000000e+00+6.000600000000e+00+7.000700000000e+00___________________\r\n",
         }) {
        It f = test.cbegin(), e = test.cend();

        Ast::Reals data;
        if (parse(f, e, gr, data)) {
            fmt::print("Parsed: {}\n", data);
        } else {
            fmt::print("Failed\n");
        }

        if (f != e) {
            std::cout << "Remaining: " << std::quoted(std::string(f, e))
                      << "\n";
        }
    }
}

打印

代码语言:javascript
复制
Parsed: {1.0001, 2.0002, 3.0003, 4.0004, 5.0005, 6.0006, 7.0007, 8.0008}
Parsed: {1.0001, 2.0002, 3.0003, 4.0004, 5.0005, 6.0006, 7.0007, 0}

我懒洋洋地处理输出格式,使用libfmt而不是再次编写向量打印cruft;Coliru还没有libfmt (或c++23)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69480328

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档