Search code examples
c++boost-spiritboost-spirit-qi

Using boost spirit expression


I have a problem with boost spirit expression.The original code is more complicated and I made a small snapshot of it. This code works as expected in debug mode and doesn't in release, parse returns false, but should return true ( I'm testing it on Visual Studio 2019)

int main() {
    namespace qi = boost::spirit::qi;

    auto X19 = qi::lit("                   ") | qi::lit(" 0.000000000000D+00");
    auto i4 = qi::uint_;
    string str = "1234                   1234";
    auto f = str.cbegin();
    auto l = str.cend();
    bool ret = qi::parse(f, l, ( i4 >> X19 >> i4 ));
}

The next snapshot works well in both modes:

int main() {
    namespace qi = boost::spirit::qi;

    auto i4 = qi::uint_;
    string str = "1234                   1234";
    auto f = str.cbegin();
    auto l = str.cend();
    bool ret = qi::parse(f, l, ( i4 >> (qi::lit("                   ") | qi::lit(" 0.000000000000D+00")) >> i4 ));
    cout << ret << endl;
    return ret;
}

What I do wrong, dividing long expression this way ? Thank you


Solution

  • You ran headlong into the the dangling temporaries trap with Spirit's Proto expressions. The short rule is:

    Don't use auto with Spirit expressions

    Your program exhibits Undefined Behaviour: see it Live On Compiler Explorer

    enter image description here


    The workarounds involve BOOST_SPIRIT_AUTO or qi::copy (which used to be available only as boost::proto::deep_copy before recent boost releases).

    Fixed

    Live On Coliru

    #include <boost/spirit/include/qi.hpp>
    
    int main() {
        namespace qi = boost::spirit::qi;
    
        auto i4 = qi::copy(qi::uint_);
        auto X19 = qi::copy(               //
              qi::lit("                   ") //
            | qi::lit(" 0.000000000000D+00")//
            );
    
        std::string str = "1234                   1234";
        auto        f = str.cbegin(), l = str.cend();
    
        bool ret = qi::parse(f, l, (i4 >> X19 >> i4));
        std::cout << std::boolalpha << ret << "\n";
    }
    

    Prints

    true
    

    BONUS

    1. Note that Spirit X3 no longer has this restriction/problem: Live On Coliru

      #include <boost/spirit/home/x3.hpp>
      
      int main() {
          namespace x3 = boost::spirit::x3;
      
          auto i4 = x3::uint_;
          auto X19 =                           //
              x3::lit("                   ")   //
              | x3::lit(" 0.000000000000D+00") //
              ;
      
          std::string str = "1234                   1234";
          auto        f = str.cbegin(), l = str.cend();
      
          bool ret = parse(f, l, (i4 >> X19 >> i4));
          std::cout << std::boolalpha << ret << "\n";
      }
      
    2. You might actually want

      qi::uint_parser<int, 10, 4, 4> i4;
      // or
      x3::uint_parser<int, 10, 4, 4> i4;
      
    3. It looks a lot as if you're parsing COBOL like (fixed-width) records, in which case I think there are much better ways to organize your grammar to make it reliable.