Boost Spirit X3 无法编译具有可变因子的重复指令
Boost Spirit X3 cannot compile repeat directive with variable factor
我正在尝试使用具有可变重复因子的 Boost Spirit X3 指令 repeat。基本思想是 header + 有效载荷,其中 header 指定有效载荷的大小。一个简单的示例“3 1 2 3”被解释为 header = 3,数据 = {1, 2, 3}(3 个整数)。
我只能从灵气文档中找到例子。它使用 boost phoenix 引用来包装变量因子:http://www.boost.org/doc/libs/1_50_0/libs/spirit/doc/html/spirit/qi/reference/directive/repeat.html
std::string str;
int n;
test_parser_attr("\x0bHello World",
char_[phx::ref(n) = _1] >> repeat(phx::ref(n))[char_], str);
std::cout << n << ',' << str << std::endl; // will print "11,Hello World"
我为 spirit x3 编写了以下简单示例,但运气不佳:
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <iostream>
namespace x3 = boost::spirit::x3;
using x3::uint_;
using x3::int_;
using x3::phrase_parse;
using x3::repeat;
using x3::space;
using std::string;
using std::cout;
using std::endl;
int main( int argc, char **argv )
{
string data("3 1 2 3");
string::iterator begin = data.begin();
string::iterator end = data.end();
unsigned int n = 0;
auto f = [&n]( auto &ctx ) { n = x3::_attr(ctx); };
bool r = phrase_parse( begin, end, uint_[f] >> repeat(boost::phoenix::ref(n))[int_], space );
if ( r && begin == end )
cout << "Parse success!" << endl;
else
cout << "Parse failed, remaining: " << string(begin,end) << endl;
return 0;
}
使用 boost 1.59.0 和 clang++(标志:-std=c++14)编译上面的代码得到以下结果:
boost_1_59_0/boost/spirit/home/x3/directive/repeat.hpp:72:47: error: no matching constructor for
initialization of 'proto_child0' (aka 'boost::reference_wrapper<unsigned int>')
typename RepeatCountLimit::type i{};
如果我硬编码 repeat(3)
而不是 repeat(boost::phoenix::ref(n))
它可以正常工作,但这不是一个可能的解决方案,因为它应该支持可变的重复因子。
使用 repeat(n)
的编译成功完成,但解析失败并显示以下输出:
“Parse failed, remaining: 1 2 3"
查看 boost/spirit/home/x3/directive/repeat.hpp:72
的源代码,它调用模板类型 RepeatCountLimit::type
变量 i
的空构造函数,然后在 for 循环期间赋值,遍历最小值和最大值。然而,由于该类型是一个引用,它应该在构造函数中初始化,因此编译失败。从以前的库版本 boost/spirit/home/qi/directive/repeat.hpp:162 看它的等效源代码是直接分配的:
typename LoopIter::type i = iter.start();
我不确定我在这里做错了什么,或者 x3 当前是否不支持可变重复因子。我将不胜感激一些帮助解决这个问题。谢谢。
据我所知,阅读源代码和邮件列表,Phoenix 根本没有集成到 X3 中:原因是 c++14 使它的大部分都过时了。
我同意这留下了一些 Qi 曾经有优雅解决方案的地方,例如eps(DEFERRED_CONDITION)
、lazy(*RULE_PTR)
(Nabialek trick),确实如此。
Spirit X3 仍在开发中,因此我们可能会看到添加了这个¹
目前,Spirit X3 拥有一种用于有状态上下文的通用工具。这实质上取代了 locals<>
,在某些情况下继承了参数,并且在这种特殊情况下也可以/使/验证元素的数量:
x3::with
²
以下是您的使用方法:
with<_n>(std::ref(n))
[ omit[uint_[number] ] >>
*(eps [more] >> int_) >> eps [done] ]
这里,_n
是一种标记类型,用于标识要用get<_n>(cxtx)
检索的上下文元素。
Note, currently we have to use a reference-wrapper to an lvalue n
because with<_n>(0u)
would result in constant element inside the context. I suppose this, too, is a QoI that may be lifted as X# matures
现在,对于语义动作:
unsigned n;
struct _n{};
auto number = [](auto &ctx) { get<_n>(ctx).get() = _attr(ctx); };
这会将解析的无符号数存储到上下文中。 (事实上,由于 ref(n)
绑定,它现在实际上不是上下文的一部分,如前所述)
auto more = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) > _val(ctx).size(); };
这里我们检查我们实际上不是 "full" - 即更多整数是 allowed
auto done = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) == _val(ctx).size(); };
这里我们检查我们是 "full" - 即 否 更多整数 allowed.
综合起来:
#include <string>
#include <iostream>
#include <iomanip>
#include <boost/spirit/home/x3.hpp>
int main() {
for (std::string const input : {
"3 1 2 3", // correct
"4 1 2 3", // too few
"2 1 2 3", // too many
//
" 3 1 2 3 ",
})
{
std::cout << "\nParsing " << std::left << std::setw(20) << ("'" + input + "':");
std::vector<int> v;
bool ok;
{
using namespace boost::spirit::x3;
unsigned n;
struct _n{};
auto number = [](auto &ctx) { get<_n>(ctx).get() = _attr(ctx); };
auto more = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) > _val(ctx).size(); };
auto done = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) == _val(ctx).size(); };
auto r = rule<struct _r, std::vector<int> > {}
%= with<_n>(std::ref(n))
[ omit[uint_[number] ] >> *(eps [more] >> int_) >> eps [done] ];
ok = phrase_parse(input.begin(), input.end(), r >> eoi, space, v);
}
if (ok) {
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout << v.size() << " elements: ", " "));
} else {
std::cout << "Parse failed";
}
}
}
打印:
Parsing '3 1 2 3': 3 elements: 1 2 3
Parsing '4 1 2 3': Parse failed
Parsing '2 1 2 3': Parse failed
Parsing ' 3 1 2 3 ': 3 elements: 1 2 3
¹ 将您的 support/voice 借给 [spirit-general] 邮件列表 :)
² 找不到合适的文档 link,但在某些示例中使用了它
我正在尝试使用具有可变重复因子的 Boost Spirit X3 指令 repeat。基本思想是 header + 有效载荷,其中 header 指定有效载荷的大小。一个简单的示例“3 1 2 3”被解释为 header = 3,数据 = {1, 2, 3}(3 个整数)。
我只能从灵气文档中找到例子。它使用 boost phoenix 引用来包装变量因子:http://www.boost.org/doc/libs/1_50_0/libs/spirit/doc/html/spirit/qi/reference/directive/repeat.html
std::string str;
int n;
test_parser_attr("\x0bHello World",
char_[phx::ref(n) = _1] >> repeat(phx::ref(n))[char_], str);
std::cout << n << ',' << str << std::endl; // will print "11,Hello World"
我为 spirit x3 编写了以下简单示例,但运气不佳:
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <iostream>
namespace x3 = boost::spirit::x3;
using x3::uint_;
using x3::int_;
using x3::phrase_parse;
using x3::repeat;
using x3::space;
using std::string;
using std::cout;
using std::endl;
int main( int argc, char **argv )
{
string data("3 1 2 3");
string::iterator begin = data.begin();
string::iterator end = data.end();
unsigned int n = 0;
auto f = [&n]( auto &ctx ) { n = x3::_attr(ctx); };
bool r = phrase_parse( begin, end, uint_[f] >> repeat(boost::phoenix::ref(n))[int_], space );
if ( r && begin == end )
cout << "Parse success!" << endl;
else
cout << "Parse failed, remaining: " << string(begin,end) << endl;
return 0;
}
使用 boost 1.59.0 和 clang++(标志:-std=c++14)编译上面的代码得到以下结果:
boost_1_59_0/boost/spirit/home/x3/directive/repeat.hpp:72:47: error: no matching constructor for
initialization of 'proto_child0' (aka 'boost::reference_wrapper<unsigned int>')
typename RepeatCountLimit::type i{};
如果我硬编码 repeat(3)
而不是 repeat(boost::phoenix::ref(n))
它可以正常工作,但这不是一个可能的解决方案,因为它应该支持可变的重复因子。
使用 repeat(n)
的编译成功完成,但解析失败并显示以下输出:
“Parse failed, remaining: 1 2 3"
查看 boost/spirit/home/x3/directive/repeat.hpp:72
的源代码,它调用模板类型 RepeatCountLimit::type
变量 i
的空构造函数,然后在 for 循环期间赋值,遍历最小值和最大值。然而,由于该类型是一个引用,它应该在构造函数中初始化,因此编译失败。从以前的库版本 boost/spirit/home/qi/directive/repeat.hpp:162 看它的等效源代码是直接分配的:
typename LoopIter::type i = iter.start();
我不确定我在这里做错了什么,或者 x3 当前是否不支持可变重复因子。我将不胜感激一些帮助解决这个问题。谢谢。
据我所知,阅读源代码和邮件列表,Phoenix 根本没有集成到 X3 中:原因是 c++14 使它的大部分都过时了。
我同意这留下了一些 Qi 曾经有优雅解决方案的地方,例如eps(DEFERRED_CONDITION)
、lazy(*RULE_PTR)
(Nabialek trick),确实如此。
Spirit X3 仍在开发中,因此我们可能会看到添加了这个¹
目前,Spirit X3 拥有一种用于有状态上下文的通用工具。这实质上取代了 locals<>
,在某些情况下继承了参数,并且在这种特殊情况下也可以/使/验证元素的数量:
x3::with
²
以下是您的使用方法:
with<_n>(std::ref(n))
[ omit[uint_[number] ] >>
*(eps [more] >> int_) >> eps [done] ]
这里,_n
是一种标记类型,用于标识要用get<_n>(cxtx)
检索的上下文元素。
Note, currently we have to use a reference-wrapper to an lvalue
n
becausewith<_n>(0u)
would result in constant element inside the context. I suppose this, too, is a QoI that may be lifted as X# matures
现在,对于语义动作:
unsigned n;
struct _n{};
auto number = [](auto &ctx) { get<_n>(ctx).get() = _attr(ctx); };
这会将解析的无符号数存储到上下文中。 (事实上,由于 ref(n)
绑定,它现在实际上不是上下文的一部分,如前所述)
auto more = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) > _val(ctx).size(); };
这里我们检查我们实际上不是 "full" - 即更多整数是 allowed
auto done = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) == _val(ctx).size(); };
这里我们检查我们是 "full" - 即 否 更多整数 allowed.
综合起来:
#include <string>
#include <iostream>
#include <iomanip>
#include <boost/spirit/home/x3.hpp>
int main() {
for (std::string const input : {
"3 1 2 3", // correct
"4 1 2 3", // too few
"2 1 2 3", // too many
//
" 3 1 2 3 ",
})
{
std::cout << "\nParsing " << std::left << std::setw(20) << ("'" + input + "':");
std::vector<int> v;
bool ok;
{
using namespace boost::spirit::x3;
unsigned n;
struct _n{};
auto number = [](auto &ctx) { get<_n>(ctx).get() = _attr(ctx); };
auto more = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) > _val(ctx).size(); };
auto done = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) == _val(ctx).size(); };
auto r = rule<struct _r, std::vector<int> > {}
%= with<_n>(std::ref(n))
[ omit[uint_[number] ] >> *(eps [more] >> int_) >> eps [done] ];
ok = phrase_parse(input.begin(), input.end(), r >> eoi, space, v);
}
if (ok) {
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout << v.size() << " elements: ", " "));
} else {
std::cout << "Parse failed";
}
}
}
打印:
Parsing '3 1 2 3': 3 elements: 1 2 3
Parsing '4 1 2 3': Parse failed
Parsing '2 1 2 3': Parse failed
Parsing ' 3 1 2 3 ': 3 elements: 1 2 3
¹ 将您的 support/voice 借给 [spirit-general] 邮件列表 :)
² 找不到合适的文档 link,但在某些示例中使用了它