boost spirit语法的不一致行为

inconsistent behavior of boost spirit grammar

我有一些语法要用于工作项目。最小可执行示例是:

#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 版本有问题,我可能会说服公司升级,但无论如何都欢迎提供解决方法。

您正在代码中调用 Undefined Behaviour

特别是在您使用 auto 存储解析器表达式的地方。包含完整表达式 ¹ 的 末尾的 Expression Template contains references to temporaries that become dangling

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

使用以下方法修复它:

  • qi::copy(或 v.1.55 IIRC 之前的 boost::proto::deep_copy
  • 使用 BOOST_SPIRIT_AUTO 而不是 BOOST_AUTO(如果您还支持 C++03,则最有帮助)
  • 使用qi::rule<>qi::grammar<>non-terminals)来键入擦除和表达式。这也会对性能产生影响,但也会提供更多功能,例如

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

Note also that Spirit X3 promises to drop there restrictions on the use with auto. It's basically a whole lot more lightweight due to the use of c++14 features. Keep in mind that it's not stable yet.

  • 显示带有 -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,;

¹(这里基本上是 "at the next semicolon";但在标准语中)