草庐IT

c++ - 仅在 Boost.Spirit.Qi 语法初始化的优化构建中出现段错误

coder 2024-02-22 原文

我在使用 Spirit Qi 编写解析器时遇到了一个奇怪的问题:我在某个地方有一个错误导致 -O 优化崩溃,但并非没有优化。它在语法的构造函数中崩溃:

template <typename Iterator>
struct math_expression_grammar : qi::grammar<Iterator, std::string()>
{
    qi::rule<Iterator, std::string()>
        expression,
        term,
        factorial,
        factor,
        pexpression,
        pfactor,
        nfactor,
        number;

    math_expression_grammar():
        math_expression_grammar::base_type(expression)
    {
        using namespace boost::spirit;
        using namespace boost::spirit::ascii;
        namespace sp = boost::spirit;
        namespace ph = boost::phoenix;

        auto sum = term[_val = sp::_1] >> lit('+') >> term[_val += sp::_1, _val += "+ "];
        auto difference = term[_val = sp::_1] >> lit('-') >> term[_val += sp::_1, _val += "- "];
        auto product = factor[_val = sp::_1] >> lit('*') >> factor[_val += sp::_1, _val += "* "];
        auto dividend = factor[_val = sp::_1] >> lit('/') >> factor[_val += sp::_1, _val += "/ "];

        expression = sum |
                     difference |
                     term;

        term = product |
               dividend |
               factor;

        pfactor = factorial.alias();
        nfactor = (lit('-') >> pfactor)[_val = sp::_1 + "n "];
        factor = nfactor | pfactor;

        pexpression = lit('(') >> expression >> lit(')');

        factorial = (pexpression | number)[_val = sp::_1] >> -lit('!')[_val += "! "];

        number = sp::double_[_val = ph::bind(stringize<double>, sp::_1) + ' '];
    }
};

我必须在 Windows 64 位的 TDM GCC 4.8.2 和 Arch Linux 64 位的 GCC 4.9.0 上测试它;两者都有同样的问题。这是 Valgrind 跟踪的相关部分,已启用优化:

==15671== Use of uninitialised value of size 8
==15671==    at 0x4040DA: void boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<char*, std::string>, std::string (), boost::spirit::unused_type, boost::spirit::unused_type, boost::spiri
t::unused_type>::define<mpl_::bool_<false>, boost::proto::exprns_::expr<boost::proto::tagns_::tag::bitwise_or, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_:
:tag::bitwise_or, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tag
ns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__norma
l_iterator<char*, std::string>, std::string (), boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::
terminal, boost::proto::argsns_::term<boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument
<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > > cons
t&>, 0l> >, 2l> const&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::terminal_ex<boost::spirit::tag::lit, boost::fusion::vecto
r1<char> > >, 0l> const&>, 2l> const&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_iterator<ch
ar*, std::string>, std::string (), boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boo
st::proto::argsns_::term<boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::sequence_eval, boost::fusion::vector<boost::phoenix::composite<boost::phoenix::plus_assign_eval, boo
st::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boo
st::fusion::void_, boost::fusion::void_, boost::fusion::void_> >, boost::phoenix::composite<boost::phoenix::plus_assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::phoen
ix::value<char const*>, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusi
on::void_> >, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_>
 > > const&>, 0l> >, 2l> const&>, 2l>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::ta
g::shift_right, boost::proto::argsns_::list2<boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::qi::rule<__gnu_cxx::__normal_itera
tor<char*, std::string>, std::string (), boost::spirit::unused_type, boost::spirit::unused_type, boost::spirit::unused_type>&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::termina
l, boost::proto::argsns_::term<boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::assign_eval, boost::fusion::vector<boost::spirit::attribute<0>, boost::spirit::argument<0>, bo
ost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_, boost::fusion::void_> > > const&>, 0l
> >, 2l> const&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::terminal, boost::proto::argsns_::term<boost::spirit::terminal_ex<boost::spirit::tag::lit, boost::fusion::vector1<char
> > >, 0l> const&>, 2l> const&, boost::proto::exprns_::expr<boost::proto::tagns_::tag::subscript, boost::proto::argsns_::list2<boost::spirit::
==15671==    by 0x404BD9: math_expression_grammar<__gnu_cxx::__normal_iterator<char*, std::string> >::math_expression_grammar() (in /home/collin/programming/parser/parser)
==15671==    by 0x401E6A: main (in /home/collin/programming/parser/parser)
==15671==  Uninitialised value was created by a stack allocation
==15671==    at 0x404672: math_expression_grammar<__gnu_cxx::__normal_iterator<char*, std::string> >::math_expression_grammar() (in /home/collin/programming/parser/parser)

这是关闭优化的整个日志:

==15686== Memcheck, a memory error detector
==15686== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==15686== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==15686== Command: ./parser
==15686==

==15686==
==15686== FILE DESCRIPTORS: 3 open at exit.
==15686== Open file descriptor 2: /dev/pts/5
==15686==    <inherited from parent>
==15686==
==15686== Open file descriptor 1: /dev/pts/5
==15686==    <inherited from parent>
==15686==
==15686== Open file descriptor 0: /dev/pts/5
==15686==    <inherited from parent>
==15686==
==15686==
==15686== HEAP SUMMARY:
==15686==     in use at exit: 0 bytes in 0 blocks
==15686==   total heap usage: 14 allocs, 14 frees, 776 bytes allocated
==15686==
==15686== All heap blocks were freed -- no leaks are possible
==15686==
==15686== For counts of detected and suppressed errors, rerun with: -v
==15686== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

两次测试之间我的代码没有任何变化。

我在实际调试问题时遇到了麻烦,因为该错误仅在 -O 优化或更高优化时出现,但不幸的是 -Og 不会出现。我确实怀疑这个错误可能在 Boost.Spirit 中,但我也很不确定;我没有发现我的代码有任何问题,但我可能遗漏了一些东西或错误地使用了 Spirit。更有经验的人可以指出我正确的方向吗?

这是完整的可编译代码:

#include <iostream>
#include <string>
#include <vector>
#include <stack>
#include <algorithm>
#include <utility>

#include <boost/mpl/vector.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_real.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>

namespace qi = boost::spirit::qi;

template <typename NumType, typename Iterator>
NumType inline parse_number(Iterator first, Iterator last)
{
    using namespace std;

    istringstream extractor(string(first, last));
    NumType num;
    extractor >> num;

    return num;
}

template <typename NumType = double, typename Iterator>
NumType eval_rpn(Iterator head, Iterator last)
{
    using namespace std;

    const char tokens[] = {'+', '-', '*', '/', '^', 'n', ' '};
    auto tokens_begin = begin(tokens), tokens_end = end(tokens);

    stack<NumType> num_stack;

    while(head != last)
    {
        auto next = find_first_of(head, last, tokens_begin, tokens_end);

        if(head != next) num_stack.push(parse_number<NumType>(head, next));

        if(next != last)
        {
            NumType temp;

            switch(*next)
            {
            case '+':
                temp = num_stack.top();
                num_stack.pop();
                num_stack.top() += temp;
                break;

            case '-':
                temp = num_stack.top();
                num_stack.pop();
                num_stack.top() -= temp;
                break;

            case '*':
                temp = num_stack.top();
                num_stack.pop();
                num_stack.top() *= temp;
                break;

            case '/':
                temp = num_stack.top();
                num_stack.pop();
                num_stack.top() /= temp;
                break;

            case '^':
                temp = num_stack.top();
                num_stack.pop();
                num_stack.top() = pow(num_stack.top(), temp);
                break;

            case 'n':
                num_stack.top() = -num_stack.top();
                break;

            default:
                break; // Do nothing
            }

            head = next+1;
        }
        else head = last;
    }

    return num_stack.top();
}

template <typename T>
std::string stringize(T x)
{
    return std::to_string(x);
}

template <typename Iterator>
struct math_expression_grammar : qi::grammar<Iterator, std::string()>
{
    qi::rule<Iterator, std::string()>
        expression,
        term,
        factorial,
        factor,
        pexpression,
        pfactor,
        nfactor,
        number;

    math_expression_grammar():
        math_expression_grammar::base_type(expression)
    {
        using namespace boost::spirit;
        using namespace boost::spirit::ascii;
        namespace sp = boost::spirit;
        namespace ph = boost::phoenix;

        auto sum = term[_val = sp::_1] >> lit('+') >> term[_val += sp::_1, _val += "+ "];
        auto difference = term[_val = sp::_1] >> lit('-') >> term[_val += sp::_1, _val += "- "];
        auto product = factor[_val = sp::_1] >> lit('*') >> factor[_val += sp::_1, _val += "* "];
        auto dividend = factor[_val = sp::_1] >> lit('/') >> factor[_val += sp::_1, _val += "/ "];

        expression = sum |
                     difference |
                     term;

        term = product |
               dividend |
               factor;

        pfactor = factorial.alias();
        nfactor = (lit('-') >> pfactor)[_val = sp::_1 + "n "];
        factor = nfactor | pfactor;

        pexpression = lit('(') >> expression >> lit(')');

        factorial = (pexpression | number)[_val = sp::_1] >> -lit('!')[_val += "! "];

        number = sp::double_[_val = ph::bind(stringize<double>, sp::_1) + ' '];
    }
};

int main()
{
    using namespace std;

    math_expression_grammar<string::iterator> g;

    string input;
    getline(cin, input);
    while(input.size())
    {
        auto first = input.begin(), last = input.end();
        cout << input << endl;

        string result;
        if(!boost::spirit::qi::parse(first, last, g, result))
        {
            cout << "Error at " << last - first << ":\n\t" << *first << endl;
        }
        else
        {
            cout << result << endl;
            cout << eval_rpn(result.begin(), result.end()) << endl;
        }

        getline(cin, input);
    }
}

最佳答案

您不能(安全地)在 Spirit 表达式模板上使用 auto。这导致了 UB。

将很快发布一个固定版本( awaiting information )。同时,请参阅:

关于c++ - 仅在 Boost.Spirit.Qi 语法初始化的优化构建中出现段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24318149/

有关c++ - 仅在 Boost.Spirit.Qi 语法初始化的优化构建中出现段错误的更多相关文章

  1. ruby - 树顶语法无限循环 - 2

    我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

  2. ruby-on-rails - 未初始化的常量 Psych::Syck (NameError) - 2

    在我的gem中,我需要yaml并且在我的本地计算机上运行良好。但是在将我的gem推送到ruby​​gems.org之后,当我尝试使用我的gem时,我收到一条错误消息=>"uninitializedconstantPsych::Syck(NameError)"谁能帮我解决这个问题?附言RubyVersion=>ruby1.9.2,GemVersion=>1.6.2,Bundlerversion=>1.0.15 最佳答案 经过几个小时的研究,我发现=>“YAML使用未维护的Syck库,而Psych使用现代的LibYAML”因此,为了解决

  3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  4. ruby-on-rails - 使用 Sublime Text 3 突出显示 HTML 背景语法中的 ERB? - 2

    所以我在关注Railscast,我注意到在html.erb文件中,ruby代码有一个微弱的背景高亮效果,以区别于其他代码HTML文档。我知道Ryan使用TextMate。我正在使用SublimeText3。我怎样才能达到同样的效果?谢谢! 最佳答案 为SublimeText安装ERB包。假设您安装了SublimeText包管理器*,只需点击cmd+shift+P即可获得命令菜单,然后键入installpackage并选择PackageControl:InstallPackage获取包管理器菜单。在该菜单中,键入ERB并在看到包时选择

  5. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  6. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

    我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

  7. ruby - 覆盖相似的方法,更短的语法 - 2

    在Ruby类中,我重写了三个方法,并且在每个方法中,我基本上做同样的事情:classExampleClassdefconfirmation_required?is_allowed&&superenddefpostpone_email_change?is_allowed&&superenddefreconfirmation_required?is_allowed&&superendend有更简洁的语法吗?如何缩短代码? 最佳答案 如何使用别名?classExampleClassdefconfirmation_required?is_a

  8. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

  9. ruby 语法糖 : dealing with nils - 2

    可能已经问过了,但我找不到它。这里有2个常见的情况(对我来说,在编程Rails时......)用ruby​​编写是令人沮丧的:"astring".match(/abc(.+)abc/)[1]在这种情况下,我得到一个错误,因为字符串不匹配,因此在nil上调用[]运算符。我想找到的是比以下内容更好的替代方法:temp="astring".match(/abc(.+)abc/);temp.nil??nil:temp[1]简而言之,如果不匹配,则简单地返回nil而不会出错第二种情况是这样的:var=something.very.long.and.tedious.to.writevar=some

  10. ruby - Ruby 语法糖有 "rules"吗? - 2

    我正在学习Ruby的基础知识(刚刚开始),我遇到了Hash.[]method.它被引入a=["foo",1,"bar",2]=>["foo",1,"bar",2]Hash[*a]=>{"foo"=>1,"bar"=>2}稍加思索,我发现Hash[*a]等同于Hash.[](*a)或Hash.[]*一个。我的问题是为什么会这样。是什么让您将*a放在方括号内,是否有某种规则可以在何时何地使用“it”?编辑:我的措辞似乎造成了一些困惑。我不是在问数组扩展。我明白了。我的问题基本上是:如果[]是方法名称,为什么可以将参数放在括号内?这看起来几乎——但不完全是——就像说如果你有一个方法Foo.d

随机推荐