在Boost.Spirit中,只需执行以下操作即可从流中读取到std::vector
:
#include<vector>
#include<boost/spirit/include/qi.hpp>
namespace sqi = boost::spirit::qi;
int main(){
std::string const v_str = "AA BB CC";
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(it, end(v_str),
(*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, v);
assert( v.size() == 3 and v[2] == "CC" );
}
但是,由于输入格式的原因,我事先知道了元素的数量,所以我应该能够保留向量中的空间。 例如,如果输入字符串为“ 3 AA BB CC”,则可以预先分配三个元素。
问题是如何将这些额外的信息传递给向量,并优化后面的push_back
(例如,避免重新分配)。
我试图在执行reserve
时将一个语义动作与该动作相关联,以开始时解析一个整数。
std::string const v_str = "3 AA BB CC";
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(it, end(v_str),
sqi::int_[([&](int i){v.reserve(i);})] >>
(*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, v);
问题在于,在执行语义操作后,该整数不会被忽略,从我的测试中,我可以看到它试图将结果(示例中的3
)推到保留后的向量中。
另一种解决方法是在phrase_parse
函数中添加另一个参数,但这似乎是过大的选择。
那么,如何在Boost.Spirit中解析某些内容,并且仅执行语义操作而不将结果发送到接收器变量?
即使可以做到这一点,我也不确定这是否是正确的方法。
答案 0 :(得分:0)
您可以创建伪造矢量,该伪造矢量将在两次插入和解析相同文本时计数:
#include<vector>
#include<boost/spirit/include/qi.hpp>
namespace sqi = boost::spirit::qi;
struct fake_vector
{
typedef std::string value_type;
fake_vector() : counter(0) {}
std::size_t end() const {return 0;};
void insert(std::size_t, std::string){ ++counter; }
std::size_t counter;
};
int main(){
std::string const v_str = "AA BB CC";
auto it = begin(v_str);
fake_vector fv;
bool r = sqi::phrase_parse(it, end(v_str), (*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, fv);
assert(fv.counter == 3);
std::vector<std::string> v;
v.reserve(fv.counter);
it = begin(v_str);
r = sqi::phrase_parse(it, end(v_str), (*sqi::lexeme[+sqi::char_("A-Z")]), sqi::space, v);
assert( v.size() == 3 and v[2] == "CC" );
}
答案 1 :(得分:0)
好吧,我似乎不得不解构Spirit的简单功能并将其全部转换为语义动作,这又在路上造成了其他问题(例如lexeme[+char_]
映射到std::vector<char>
而不是std::string
。
{
std::string const v_str = "AA BB CC";
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(it, end(v_str),
(*(sqi::lexeme[(+sqi::char_("A-Z"))][([&](auto&& s){v.emplace_back(begin(s), end(s));})])), sqi::space);
assert( v.size() == 3);
assert( v[2] == "CC" );
}
{
std::string const v_str = "3 AA BB CC";
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(it, end(v_str),
sqi::int_[([&](int i){v.reserve(i);})] >>
(*(sqi::lexeme[(+sqi::char_("A-Z"))][([&](auto&& s){v.emplace_back(begin(s), end(s));})])), sqi::space);
assert( v.size() == 3 );
assert( v[2] == "CC" );
}
由于这修改了phrase_parse
的最后一个参数,我不妨放置一个虚拟int
。
答案 2 :(得分:0)
借助@sehe和@drus指向的链接并找到有关qi::omit
的链接,我意识到我可以关联一个语义动作,然后忽略结果。
我必须处理的格式是多余的(大小与元素数量是多余的),因此在任何情况下我都必须 省略。
using namespace sqi;
std::string const v_str = "3 AA BB CC";
{
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(
it, end(v_str),
omit[int_] >> *lexeme[+(char_-' ')],
space, v
);
assert( v.size() == 3 and v[2] == "CC" );
}
但这并不意味着我不能将省略(冗余)的部分用于优化目的或一致性检查。
{
std::vector<std::string> v;
auto it = begin(v_str);
bool r = sqi::phrase_parse(
it, end(v_str),
omit[int_[([&](int n){v.reserve(n);})]] >> *lexeme[+(char_-' ')],
space, v
);
assert( v.size() == 3 and v[2] == "CC" );
}
我同意语义动作是邪恶的,但是在我看来,只有当它们改变接收对象的状态时,它才是邪恶的。
有人可以说reserve
不会改变向量的状态。
实际上,通过这种方式,我可以通过reserve
优化内存使用,还可以通过使用repeat
而不是无限制的kleene *
优化解析器的执行。 (Apparently repeat
can be more efficient)。
{
std::vector<std::string> v;
auto it = begin(v_str);
int n;
bool r = sqi::phrase_parse(
it, end(v_str),
omit[int_[([&](int nn){v.reserve(n = nn);})]] >> repeat(phx::ref(n))[lexeme[+(char_-' ')]],
space, v
);
assert( n == v.size() and v.size() == 3 and v[2] == "CC" );
}
(取消phx::ref
的使用非常重要,因为必须延迟对n
的评估)