助推精神语法不一致的行为

inconsistent behavior of boost spirit grammar

本文关键字:不一致 语法      更新时间:2023-10-16

我有一个小语法,我想在一个工作项目中使用。最小可执行示例如下:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#pragma GCC diagnostic ignored "-Wunused-variable"
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/qi_grammar.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>
#pragma GCC diagnostic pop // pops
#include <iostream>
int main()
{
    typedef  unsigned long long ull;
    std::string curline = "1;2;;3,4;5";
    std::cout << "parsing:  " << curline << "n";
    namespace qi = boost::spirit::qi;
    auto ids = -qi::ulong_long % ','; // '-' allows for empty vecs.
    auto match_type_res = ids % ';' ;
    std::vector<std::vector<ull> > r;
    qi::parse(curline.begin(), curline.end(), match_type_res, r);
    std::cout << "got:      ";
    for (auto v: r){
        for (auto i : v)
            std::cout << i << ",";
        std::cout << ";";
    }
    std::cout <<"n";
}

在我个人的机器上,这会产生正确的输出:解析:1,2,3,4,5有:1,,2,,3,4,5,

但在工作中它产生:解析:1,2,3,4,5有:1,,2,,3,

换句话说,只要长整数向量中有多个元素,它就无法解析。

现在,我已经确定工作系统正在使用boost 1.56,而我的私人计算机正在使用1.57。这是原因吗?

知道我们这里有一些真正的精神专家在堆栈溢出,我希望有人可能知道这个问题是从哪里来的,或者至少可以缩小我需要检查的东西的数量。

如果是boost版本的问题,我可能会说服公司升级,但在任何情况下,一个解决方案将是受欢迎的。

你在代码中调用了未定义行为。

特别是在使用auto存储解析器表达式的地方。表达式模板包含对临时变量的引用,这些临时变量会在包含完整表达式的末尾悬垂。

UB意味着任何事情都可能发生。两个编译器都是正确的!最好的部分是,您可能会看到不同的行为取决于所使用的编译器标志。

可以使用:

  • qi::copy(或v1.55 IIRC之前的boost::proto::deep_copy)
  • 使用BOOST_SPIRIT_AUTO而不是BOOST_AUTO(如果您也支持c++ 03,则非常有用)
  • 使用qi::rule<>qi::grammar<>(非终结符)对表达式进行类型擦除。这对性能也有影响,但也提供了更多的特性,如

      递归规则
    • 本地属性和继承属性
    • 声明的船长(方便,因为规则可以隐式lexeme[](见这里)
    • 更好的代码组织。

还请注意,Spirit X3承诺取消对使用auto的限制。由于使用了c++14的特性,它基本上更加轻量级。请记住它还不稳定

  • 显示-O2的GCC显示未定义的结果;Live On Coliru

  • 固定版本:

Live On Coliru

//#pragma GCC diagnostic push
//#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
//#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
//#pragma GCC diagnostic ignored "-Wunused-variable"
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/qi.hpp>
//#pragma GCC diagnostic pop // pops
#include <iostream>
int main() {
    typedef  unsigned long long ull;
    std::string const curline = "1;2;;3,4;5";
    std::cout << "parsing: '" << curline << "'n";
    namespace qi = boost::spirit::qi;
#if 0 // THIS IS UNDEFINED BEHAVIOUR:
    auto ids     = -qi::ulong_long % ','; // '-' allows for empty vecs.
    auto grammar = ids % ';';
#else // THIS IS CORRECT:
    auto ids     = qi::copy(-qi::ulong_long % ','); // '-' allows for empty vecs.
    auto grammar = qi::copy(ids % ';');
#endif
    std::vector<std::vector<ull> > r;
    qi::parse(curline.begin(), curline.end(), grammar, r);
    std::cout << "got:      ";
    for (auto v: r){
        for (auto i : v)
            std::cout << i << ",";
        std::cout << ";";
    }
    std::cout <<"n";
}

打印(也使用GCC -O2!):

parsing: '1;2;;3,4;5'
got:      1,;2,;;3,4,;5,;

¹(这里基本上是"在下一个分号";但在标准中)