Boost.Qi规则是否始终通过引用保存在使用它们的表达式中?

时间:2016-11-14 23:19:42

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

假设我有一个简单类型foo

struct foo { void bar(int) { // do something } };

我想使用Boost.Qi解析字符串中的整数字段,并使用结果值调用foo::bar()。这可以通过语义操作来完成,如下所示:

std::string str;
bar b1, b2;
boost::spirit::phrase_parse(str.begin(), str.end(),
    boost::spirit::int_[boost::bind(&foo::bar, &b1, _1)] >>
    boost::spirit::int_[boost::bind(&foo::bar, &b2, _1)]);

这有很多重复,所以我可以删除一些这样的样板:

template <typename Iterator>
boost::spirit::qi::rule<Iterator, int(), boost::spirit::qi::space> parse_bar(bar *b)
{
    return boost::spirit::int_[boost::bind(&foo::bar, _1)];
}

std::string str;
bar b1, b2;
boost::spirit::phrase_parse(str.begin(), str.end(),
    parse_bar(&b1) >> parse_bar(b2));

这看起来更好而且有效。但是,有时我想保存解析器表达式以供稍后使用(例如,如果我想在lambda函数中通过值捕获它来延迟使用)。在原始示例中,我可以执行以下操作:

std::string str;
bar b1, b2;
auto parser = boost::proto::deep_copy(
    boost::spirit::int_[boost::bind(&foo::bar, &b1, _1)] >>
    boost::spirit::int_[boost::bind(&foo::bar, &b2, _1)]);
auto lambda = [parser]() { boost::spirit::phrase_parse(str.begin(), str.end(), parser); };

// some time later
lambda();

这也有效(尽管需要调用boost::proto::deep_copy()来确保AST中的所有内部节点都不被引用保留)。我的第一印象是我可以应用相同的rule重构来简化上面的代码:

std::string str;
bar b1, b2;
auto parser = boost::proto::deep_copy(parse_bar(&b1) >> parse_bar(&b2));
auto lambda = [parser]() { boost::spirit::phrase_parse(str.begin(), str.end(), parser); };

// some time later
lambda();

但是,这会导致后来调用lambda()时出现问题。根据我的调试,即使在调用rule之后,parse_bar返回的deep_copy()对象似乎始终通过引用保存在表达式中。由于rule对象是包含对deep_copy()的调用的行中的右值,因此在稍后调用phrase_parse()期间对它们的引用无效。

这似乎表明rule个对象始终是lvalues,其生命周期至少与引用它们的表达式的生命周期相匹配。这是真的?我想我可能误解了图书馆的一个关键概念,并试图以“错误的方式”这样做。在我的申请中,我没有正式的语法;我正在寻找一种简单,紧凑的方法来内联定义大量类似的解析器表达式,并使用语义动作调用绑定的成员函数,如上所示。

1 个答案:

答案 0 :(得分:3)

简单回答:是的。

<!-- reads the rest of your question -->

您的代码示例中存在许多不准确之处。

第一个片段

  1. bar b1, b2; // probably meant foo?
  2. boost::spirit::phrase_parse不存在,boost::spirit::int_
  3. 也不存在
  4. boost::bind不能作为语义动作
  5. phrase_parse需要一名船长,您不提供
  6.   

    修正:

        std::string str = "123 234";
        qi::phrase_parse(str.begin(), str.end(),
                qi::int_[boost::bind(&foo::bar, &b1, _1)] >>
                qi::int_[boost::bind(&foo::bar, &b2, _1)],
                qi::space);
    

    第二个片段

    在随后的示例中,您有barfoo混合,您将qi::space作为类型参数传递,bind无法绑定到b等。不重复上述内容并跳过明显的错误

    1. 存在一个基本问题,即模板函数需要用作parse_bar<std::string::const_iterator>。我会说它看起来不再“好看”。
    2. 规则声明int()意味着您公开属性,但它从未被分配或使用
    3.   

      修正:

      template <typename Iterator>
      static qi::rule<Iterator, qi::space_type> parse_bar(foo *b) {
          return qi::int_ [boost::bind(&foo::bar, b, _1)];
      }
      
      int main() {
          foo b1, b2;
          using It = std::string::const_iterator;
      
          std::string const str = "234 345";
          qi::phrase_parse(str.begin(), str.end(), parse_bar<It>(&b1) >> parse_bar<It>(&b2), qi::space);
      

      第三个片段

      除了str之外,大多数问题都没有被捕获。

      第四个片段

      是。请参阅我的答案的第一行

      解决方案

      在盒子外面思考。您需要“可爱”语法将解析器绑定到带外属性。

      请注意

      • 这就像回归Spirit V1(经典)设计。它不是图书馆的精神 [原文如此],可以这么说
      • 如果您只想这样,请使用继承的属性?

        std::string const str = "111 222";
        
        // use phoenix action 
        auto foo_bar = std::mem_fn(&foo::bar);
        px::function<decltype(foo_bar)> foo_bar_(foo_bar);
        
        // with inherited attributes
        qi::rule<It, void(foo*)> _foo = qi::int_ [ foo_bar_(qi::_r1, qi::_1) ];
        
        auto lambda = [_foo,&b1,&b2,str] { qi::phrase_parse(str.begin(), str.end(), _foo(&b1) >> _foo(&b2), qi::space); };
        
        // some time later
        lambda();
        
        std::cout << b1 << ", " << b2 << "\n";
        
      • 如果您希望为foo等类型提供“魔法”支持,并且在逻辑上类似于分配/转化,请使用以下特征:http://www.boost.org/doc/libs/1_62_0/libs/spirit/doc/html/spirit/advanced/customize.html

      现场演示

      我总是想添加一个现场演示,所以你去了:

      <强> Live On Wandbox

      #include <boost/spirit/include/qi.hpp>
      #include <boost/spirit/include/phoenix.hpp>
      #include <boost/bind.hpp>
      #include <iostream>
      
      namespace qi = boost::spirit::qi;
      namespace px = boost::phoenix;
      
      struct foo { 
          int value = 42;
          void bar(int i) { value = i; } 
          friend std::ostream& operator<<(std::ostream& os, foo const& f) { return os << "{" << f.value << "}"; }
      }; 
      
      template <typename Iterator>
      static qi::rule<Iterator, qi::space_type> parse_bar(foo *b) {
          return qi::int_ [boost::bind(&foo::bar, b, _1)];
      }
      
      int main() {
          foo b1, b2;
          using It = std::string::const_iterator;
      
          // snippet 1
          {
              std::string str = "123 234";
              qi::phrase_parse(str.begin(), str.end(),
                      qi::int_[boost::bind(&foo::bar, &b1, _1)] >>
                      qi::int_[boost::bind(&foo::bar, &b2, _1)],
                      qi::space);
      
              std::cout << b1 << ", " << b2 << "\n";
          }
      
          // snippet 2
          {
              std::string const str = "234 345";
              qi::phrase_parse(str.begin(), str.end(), parse_bar<It>(&b1) >> parse_bar<It>(&b2), qi::space);
      
              std::cout << b1 << ", " << b2 << "\n";
          }
      
          // snippet 3
          {
              std::string const str = "345 456";
      
              auto parser = boost::proto::deep_copy(
                      qi::int_[boost::bind(&foo::bar, &b1, _1)] >>
                      qi::int_[boost::bind(&foo::bar, &b2, _1)]);
      
              auto lambda = [parser,str]() { qi::phrase_parse(str.begin(), str.end(), parser, qi::space); };
      
              // some time later
              lambda();
      
              std::cout << b1 << ", " << b2 << "\n";
          }
      
          // snippet 4
          {
              std::string const str = "456 567";
              auto parser = boost::proto::deep_copy(parse_bar<It>(&b1) >> parse_bar<It>(&b2));
              auto lambda = [parser=qi::copy(parser), str]() { qi::phrase_parse(str.begin(), str.end(), parser, qi::space); };
      
              // some time later
              //lambda(); 
              //// no workey
          }
      
          // SOLUTIONS
          {
              std::string const str = "111 222";
      
              // use phoenix action 
              auto foo_bar = std::mem_fn(&foo::bar);
              px::function<decltype(foo_bar)> foo_bar_(foo_bar);
      
              // with inherited attributes
              qi::rule<It, void(foo*)> _foo = qi::int_ [ foo_bar_(qi::_r1, qi::_1) ];
      
              auto lambda = [_foo,&b1,&b2,str] { qi::phrase_parse(str.begin(), str.end(), _foo(&b1) >> _foo(&b2), qi::space); };
      
              // some time later
              lambda();
      
              std::cout << b1 << ", " << b2 << "\n";
          }
      }