【问题标题】:boost spirit, phoenix::push_back and function in semantic action提升精神,phoenix::push_back 和语义动作中的功能
【发布时间】:2019-04-27 01:12:54
【问题描述】:

我尝试了一个小测试用例,它会接受一个范围,例如 [5-3],然后将 3、4、5 推入一个向量。我只能考虑使用语义动作方法。但是,我使用 phoenix::push_back 的编码方式似乎不起作用。如果我只是按一个数字(如测试 3 中的“3”)或占位符(测试 2)。精神会工作。但是如果我使用 for 循环来推动。那么大小就会是0。基本上,什么都没有。

#include <fstream>
#include <iostream>
#include <vector>

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;

struct varVec
{
  std::vector<unsigned int> uintVec;
};

BOOST_FUSION_ADAPT_STRUCT(varVec,
                          (std::vector<unsigned int>, uintVec))

template<typename Iterator, typename Skipper>
struct listParser : public qi::grammar<Iterator,
                                       varVec(),
                                       Skipper>
{
  listParser() : listParser::base_type(varVecParse)
  {
    using namespace qi;

    varPair =
      uint_ [_a = _1]
      > '-'
      > uint_
      [
        // test 1
        px::bind([](uint lb, uint ub) {
            if (ub < lb) {
              uint temp = ub; ub  = lb; lb = temp; }
            for (unsigned int i = lb; i <= ub; i++)
            {
              px::push_back(qi::_val, i);
              std::cout << "i = " << i << std::endl;
            }
          },
          // parameters
          qi::_a, qi::_1)

        // test 2
        // px::push_back(_val, _1)
        // test 3
        // px::push_back(_val, 3)
        ]
      ;

    varVecParse = '['
      >> varPair
      >> ']'
      ;
  }

  qi::rule<Iterator, std::vector<unsigned int>(), qi::locals<unsigned int>,
           Skipper> varPair;
  qi::rule<Iterator, varVec(), Skipper> varVecParse;
};

int main()
{
  std::string input ("[ 6- 4]\n");
  std::string::const_iterator begin = input.begin();
  std::string::const_iterator end = input.end();

  listParser<std::string::const_iterator, qi::space_type> parser;

  varVec result;

  bool success = qi::phrase_parse(begin, end, parser, qi::space, result);

  unsigned int size = result.uintVec.size();
  std::cout << "size = " << size << std::endl;
  if (size > 0)
    std::cout << "val = " << result.uintVec[0] << std::endl;
  return 0;
}

所以功能是使范围为升序并将其推入无符号整数向量中。我猜语义动作中的函数有问题,但不确定是什么问题。

【问题讨论】:

标签: boost-spirit-qi


【解决方案1】:

您需要记住,尽管 Boost.Phoenix 表达式看起来是“普通”表达式,但实际上它们的行为类似于 lambda,它们需要使用所需的任何参数调用才能执行。视为简化的example

std::vector<int> val{};
using px::placeholders::arg1;

px::push_back(px::ref(val),1);
std::cout << val.size() << "\n"; //0

px::push_back(px::ref(val),1)();
std::cout << val.size() << "\n"; //1

px::push_back(arg1,1);
std::cout << val.size() << "\n"; //1

px::push_back(arg1,1)(val);
std::cout << val.size() << "\n"; //2

您的情况类似于arg1 示例(但qi::_val 稍微复杂一些)。当在解析器语义操作中使用 Phoenix 表达式时,Spirit 会使用它们需要的参数“自动”调用它们,而在您的示例中您没有,并且 uints 从未插入向量中。 您不应该将 Phoenix 代码与“普通”代码混合使用。所以你需要摆脱px::push_back(参见Wandbox):

#include <fstream>
#include <iostream>
#include <vector>

#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;

struct varVec
{
  std::vector<unsigned int> uintVec;
};

BOOST_FUSION_ADAPT_STRUCT(varVec,
                          (std::vector<unsigned int>, uintVec))

template<typename Iterator, typename Skipper>
struct listParser : public qi::grammar<Iterator,
                                       varVec(),
                                       Skipper>
{
  listParser() : listParser::base_type(varVecParse)
  {
    using namespace qi;

    varPair =
      uint_ [_a = _1]
      > '-'
      > uint_
      [ //                 ADDED
        // test 1   vvvvvvvvvvvvvvvvvvvvvv
        px::bind([](std::vector<uint>& val, uint lb, uint ub) {
            if (ub < lb) {
              uint temp = ub; ub  = lb; lb = temp; }
            for (unsigned int i = lb; i <= ub; i++)
            {
              val.push_back(i); //<---CHANGED
              std::cout << "i = " << i << std::endl;
            }
          },
          // parameters
          qi::_val, qi::_a, qi::_1)
        //^^^^^^^^^
        // ADDED

        ]
      ;

    varVecParse = '['
      >> varPair
      >> ']'
      ;
  }

  qi::rule<Iterator, std::vector<unsigned int>(), qi::locals<unsigned int>,
           Skipper> varPair;
  qi::rule<Iterator, varVec(), Skipper> varVecParse;
};

int main()
{
  std::string input ("[ 6- 4]\n");
  std::string::const_iterator begin = input.begin();
  std::string::const_iterator end = input.end();

  listParser<std::string::const_iterator, qi::space_type> parser;

  varVec result;

  bool success = qi::phrase_parse(begin, end, parser, qi::space, result);

  unsigned int size = result.uintVec.size();
  std::cout << "size = " << size << std::endl;
  if (size > 0)
    std::cout << "val = " << result.uintVec[0] << std::endl;
  return 0;
}

另一种可能性(不推荐)是一直使用 Boost.Phoenix(参见Wandbox):

...
qi::_1_type const upper;
qi::_a_type const lower;
px::local_names::_i_type const cont;

varPair =
  uint_ [lower = _1]
  > '-'
  > uint_
  [
    px::if_(upper<lower)[px::swap(lower,upper)],
    px::let(cont=lower)
    [
        px::for_(px::nothing,cont<=upper,++cont)
        [
            px::push_back(qi::_val,cont),
            px::ref(std::cout) << "i = " << cont << std::endl
        ]
     ]
   ]
  ;
...

PS:另一种可能性是在解析后的一个步骤中处理向量的生成。这样,在解析过程中,您只需存储范围的边界,然后您就可以轻松生成实际需要的向量。

【讨论】:

  • 我觉得“它们实际上是 lambdas”的措辞可以大大减少混淆
  • 您可以随意更改它。英语不是我的母语,这表明了。
  • 不确定。我会称它们为“惰性(多态)可调用对象” - 这不是很清楚,但可以避免混淆,因为显然它们实际上是 不是 lambdas。
猜你喜欢
  • 1970-01-01
  • 2011-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-06
相关资源
最近更新 更多