【问题标题】:inconsistent behavior of boost spirit grammar [duplicate]提升精神语法的不一致行为[重复]
【发布时间】:2015-07-08 12:58:09
【问题描述】:

我想在工作项目中使用一些语法。一个最小的可执行示例是:

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

【问题讨论】:

  • For reference, here's the list of changes from 1.56.0 to 1.57.0。我没有看到 Spirit 有任何变化,所以我不知道该建议什么。
  • 这些版本之间的库中没有相关更改。
  • @Cornstalks,很好发现。我没有意识到这是我的问题。
  • @Cornstalks 我认为回想起来标记重复项是不公平的。请注意,我在回答后添加了auto 标签以帮助搜索索引。 SO 很有用因为我们根据症状/信号组织问题,而不是根本原因(这就是标签的用途:根本原因是c++!)。在这种情况下,也许 *this 是正确的副本。
  • 致 OP:如果您有兴趣,我是这样处理这个问题的:from 38:00 onwards 当时我正在现场编码。您会注意到我很快意识到不一致的输出带有未定义行为的味道。下一次,您可以在搜索词中使用它——尽管找出 UB 的确切来源特别棘手。另请注意,@Cornstalks,虽然我很快发现了问题,但我花了大约 20 分钟将其作为高质量的答案。

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


【解决方案1】:

您正在代码中调用Undefined Behaviour

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

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

使用以下方法修复它:

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

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

另请注意,Spirit X3 承诺放弃对自动使用的限制。由于使用了 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,;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多