提升精神语法的不一致行为

时间:2015-07-08 12:58:09

标签: c++ parsing boost auto boost-spirit

我想要用于工作项目的语法。最低可执行示例是:

#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。这是原因吗?

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

如果升级版本出现问题,我可能会说服公司进行升级,但无论如何都会欢迎解决方法。

1 个答案:

答案 0 :(得分:2)

您在代码中调用Undefined Behaviour

特别是在您使用auto存储解析器表达式的位置。 Expression Template包含对包含完整表达式¹末尾dangling的临时对象的引用。

UB暗示任何事情都可能发生。两个编译器都是对的!最好的部分是,根据使用的编译器标志,您可能会看到不同的行为。

使用以下方法修复:

  • qi::copy(或boost::proto::deep_copy v.1.55 IIRC)
  • 使用BOOST_SPIRIT_AUTO代替BOOST_AUTO(如果您也支持C ++ 03,则非常有帮助)
  • 使用qi::rule<>qi::grammar<>non-terminals)进行类型擦除和表达式。这也会对性能产生影响,但也会提供更多功能,例如

    • 递归规则
    • 本地人和继承的属性
    • 宣布了船长(方便,因为规则可以隐式lexeme[](见here
    • 更好的代码组织。
  

另请注意,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,;

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