语义操作后丢弃解析结果

时间:2019-04-25 22:29:57

标签: c++ boost-spirit boost-spirit-qi semantic-actions

在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中解析某些内容,并且仅执行语义操作而不将结果发送到接收器变量?

即使可以做到这一点,我也不确定这是否是正确的方法。

3 个答案:

答案 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的评估)