振奋精神,phoenix::push_back 并在语义动作中发挥作用
boost spirit, phoenix::push_back and function in semantic action
我尝试了一个小的测试用例,它将接受一个范围,例如 [5-3],然后将 3、4、5 推入一个向量。我只能考虑使用语义动作方法。但是,我使用 phoenix::push_back 的编码方式似乎不起作用。如果我只是按下一个数字(如测试 3 中的“3”)或占位符(测试 2)。精神会起作用。但是,如果我使用 for 循环来推送。那么size就是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.Phoenix 表达式看起来是 "normal" 表达式,它们实际上表现得像 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 "automatically" 会使用它们需要的参数调用它们,而在您的示例中您不需要,并且 uint
从未插入向量中。
您不应该将 Phoenix 代码与 "normal" 代码混合使用。所以你需要去掉 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:另一种可能性是在解析后的一个步骤中处理向量的生成。这样在解析期间您只需存储范围的边界,然后您可以轻松生成您实际需要的向量。
我尝试了一个小的测试用例,它将接受一个范围,例如 [5-3],然后将 3、4、5 推入一个向量。我只能考虑使用语义动作方法。但是,我使用 phoenix::push_back 的编码方式似乎不起作用。如果我只是按下一个数字(如测试 3 中的“3”)或占位符(测试 2)。精神会起作用。但是,如果我使用 for 循环来推送。那么size就是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.Phoenix 表达式看起来是 "normal" 表达式,它们实际上表现得像 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 "automatically" 会使用它们需要的参数调用它们,而在您的示例中您不需要,并且 uint
从未插入向量中。
您不应该将 Phoenix 代码与 "normal" 代码混合使用。所以你需要去掉 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:另一种可能性是在解析后的一个步骤中处理向量的生成。这样在解析期间您只需存储范围的边界,然后您可以轻松生成您实际需要的向量。