【问题标题】:Segfault trying to use boost::spirit::qi parser in a class尝试在类中使用 boost::spirit::qi 解析器的段错误
【发布时间】:2016-06-27 21:08:02
【问题描述】:

我一直在使用 boost::spirit 编写一些解析器代码并开始遇到段错误。

我已经尽可能地简化了我的代码,以便于发布,见下文。

解析器回调到addModule时,在int的pushback过程中发生segfault。

Valgrind 声称向量 v_modules 未初始化。在代码的前面,我可以看到它已初始化,因此我假设正在发生一些内存垃圾。我已经尝试用这个较小的测试用例重写它很多次,但无济于事。任何帮助表示赞赏!

verilog.cpp:

#include "verilog.h"
#include <string>
#include <boost/spirit/include/qi.hpp>

Verilog::Verilog() {
  m_parser.verilog = this;
}
Verilog::~Verilog(){}

void Verilog::parse(string contents) {
  string::const_iterator iter = contents.begin();
  string::const_iterator end = contents.end();
  bool r = phrase_parse(iter,end,m_parser,boost::spirit::ascii::space);
}

void Verilog::addModule() {
  int new_mod = 1;
  v_modules.push_back(new_mod);
}

int main()
{
  Verilog* verilog = new Verilog();
  string contents = "hello";
  verilog->parse(contents);
}

verilog.h

#ifndef VERILOG_H
#define VERILOG_H

#include <iostream>
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/bind.hpp>

using namespace std;
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

class Verilog
{

 public:
  Verilog();
  ~Verilog();
  void parse(string contents);
  void addModule() ;

  template <typename Iterator>
    struct verilog_parser : qi::grammar<Iterator, ascii::space_type>  
    {
    verilog_parser() : verilog_parser::base_type(module)
        {
          module = qi::eps[boost::bind(&Verilog::addModule, verilog)];
        }

      qi::rule<Iterator, ascii::space_type> module;

      Verilog* verilog;

    };

 private:
  std::vector<int>    v_modules;
  verilog_parser<string::const_iterator> m_parser;

};

#endif

【问题讨论】:

    标签: c++ boost boost-spirit boost-spirit-qi


    【解决方案1】:

    您正在使用 boost::bind,这会产生一个临时函数对象,该对象引用 verilog 成员在语法构造期间指向的任何内容。

    那是行不通的。

    您需要一个 phoenix lazy actor,如果您希望它在从 Verilog 构造函数中设置它后获取更改的值,则最好通过 _reference 引用 this-&gt;verilog

    说实话,代码看起来有点笨拙。为什么不使用 Spirit 的属性兼容性规则为您自动构建向量(或列表、集合、地图...等)?

    这里有一个修复:

    #include <boost/spirit/include/phoenix.hpp>
    namespace phx = boost::phoenix;
    
    // ... later
    
                module = qi::eps[phx::bind(&Verilog::addModule, phx::ref(verilog))];
    

    请注意,这仍然会在main 中留下泄露的 Verilog 实例。为什么在现代 C++ 中使用new

    整合它:

    Live On Coliru

    #include <iostream>
    #include <string>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/bind.hpp>
    
    using namespace std;
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    namespace phx = boost::phoenix;
    
    class Verilog {
    
      public:
        Verilog();
        ~Verilog();
        void parse(string contents);
        void addModule();
    
        template <typename Iterator> struct verilog_parser : qi::grammar<Iterator, ascii::space_type> {
            verilog_parser() : verilog_parser::base_type(module) {
                module = qi::eps[phx::bind(&Verilog::addModule, phx::ref(verilog))];
            }
    
            qi::rule<Iterator, ascii::space_type> module;
    
            Verilog *verilog;
        };
    
      private:
        std::vector<int> v_modules;
        verilog_parser<string::const_iterator> m_parser;
    };
    
    #include <string>
    #include <boost/spirit/include/qi.hpp>
    
    Verilog::Verilog() { m_parser.verilog = this; }
    Verilog::~Verilog() {}
    
    void Verilog::parse(string contents) {
        string::const_iterator iter = contents.begin();
        string::const_iterator end = contents.end();
        bool r = phrase_parse(iter, end, m_parser, boost::spirit::ascii::space);
    }
    
    void Verilog::addModule() {
        int new_mod = 1;
        v_modules.push_back(new_mod);
    }
    
    int main() {
        Verilog verilog;
        string contents = "hello";
        verilog.parse(contents);
    }
    

    【讨论】:

    • 谢谢!那行得通。我想回答你的问题:代码很笨拙,因为我对 C++ 的经验并不丰富。我正在构建的数据结构要复杂得多,我先用各种类创建它,然后再添加解析器。您在此处看到的大部分内容中都不存在。我应该做什么而不是新的?
    • 只创建一个实例,比如int x;而不是int* x = new int;
    • @Tom 尽可能避免new,因为它之后不会自行清理。正如 sehe 所说,使用 int x; 样式声明,因为它会在您留下代码块时进行清理(在我知道的实现中)。
    • @Aaron3468 在所有符合标准的实现中(并且提供的析构函数足以满足您的类型:))
    • 差不多。智能指针更安全。 (std::unique_ptrstd::shared_ptr)。更好的是,考虑一个自动存储持续时间适合要求的实例化点。 /cc @Aaron3468
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多