【问题标题】:Using the auto_ expression in boost::spirit with std::vectors将 boost::spirit 中的 auto_ 表达式与 std::vectors 一起使用
【发布时间】:2015-01-25 01:23:49
【问题描述】:

我对 boost::spirit 还是很陌生。我想将一串逗号分隔的对象解析为 std::vector (类似于教程中的)。字符串可以是不同的类型(在编译时已知):整数,如"1,2,3"、字符串"Apple, Orange, Banana" 等。 我希望所有类型都有一个统一的接口。

如果我解析单个元素,我可以使用auto_ 表达式。 是否有可能与向量有类似的接口? 我可以定义一个规则,给定一个模板参数,实际上可以解析这个向量吗?

这是一个简单的示例代码(由于最后一次调用phrase_parse而无法编译):

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>

#include <iostream>
#include <vector>
#include <boost/spirit/include/qi_auto.hpp>


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

using qi::auto_;
using qi::phrase_parse;
using ascii::space;
using phoenix::push_back;


int main()
{
    std::string line1 = "3";
    std::string line2 = "1, 2, 3";

    int v;
    std::vector<int> vector;

    typedef std::string::iterator stringIterator;

    stringIterator first = line1.begin();
    stringIterator last  = line1.end();

    namespace qi    = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;

    bool r1 = qi::phrase_parse( first,
                                last,
                                qi::auto_,
                                ascii::space,
                                v );

    first = line2.begin();
    last  = line2.end();

    //The following call is wrong!
    bool r2 = qi::phrase_parse( first,
                                last,
                              //  Begin grammar
                               (
                                qi::auto_[push_back(phoenix::ref(vector), qi::_1)]
                                >> *(',' >> qi::auto_[push_back(phoenix::ref(vector),qi::_1)])
                               ),
                              //  End grammar
                                ascii::space, 
                                vector);
    return 0;
}

更新

我找到了一个解决方案,在解析之前知道向量大小的情况下。另一方面,我不能使用语法*( ',' &gt;&gt; qi::auto_ )

#include <boost/spirit/include/qi.hpp>

namespace qi     = boost::spirit::qi;

int main()
{
    std::string s = "1, 2, 3";

    std::vector<int> vector;
    //This works
    qi::phrase_parse(s.begin(), s.end(), qi::auto_ >> ',' >> qi::auto_  >> ',' >> qi::auto_  , qi::blank, vector);
    //This does not compile
    qi::phrase_parse(s.begin(), s.end(), qi::auto_ >> *( ',' >> qi::auto_ ) , qi::blank, vector);

    for(int i = 0; i < vector.size() ; i++)
        std::cout << i << ": " << vector[i] << std::endl;

    return 0;
}

此外,使用auto_,我无法解析字符串。是否可以定义模板函数,通过模板参数推导出语法?

template< typename T >
void MyParse(std::string& line, std::vector<T> vec)
{
     qi::phrase_parse( line.begin(), 
                       line.end(), 
                       /* 
                       How do I define a grammar based on T
                       such as: 
                          double_ >> *( ',' >> double_ )    for T = double
                       +qi::alnum >> *( ',' >> +qi::alnum ) for T = std::string
                       */, 
                       qi::blank, 
                       vec);
}

【问题讨论】:

  • 忽略std::string的问题,你要使用的是list parserauto_%',')。
  • 查看示例herethis 似乎有效(如果您愿意,可以将+(char_-',') 更改为+alnum)。
  • @cv_and_he 它就像一个魅力! list parser 的精神文档声称对于`a % b`:“这相当于a >> *(b >> a)”。但是使用 `auto_` 就不同了,因为它甚至不能编译。
  • auto_ 不适合 std::string,因为它可以匹配任何东西,真的
  • @cv_and_he 我想出了一种稍微不那么侵入性的方法来处理std::stringauto_ 我认为在我更新的答案中。您可能也应该发布基于create_parser&lt;&gt; 的版本作为答案...值得点赞。

标签: c++ boost boost-spirit boost-spirit-qi boost-phoenix


【解决方案1】:

auto_ 支持开箱即用的容器属性:

Live On Coliru

std::istringstream iss("1 2 3 4 5; 6 7 8 9;");
iss.unsetf(std::ios::skipws);

std::vector<int> i;
std::vector<double> d;

if (iss >> qi::phrase_match(qi::auto_ >> ";" >> qi::auto_, qi::space, i, d))
{
    for (auto e:i) std::cout << "int: " << e << "\n";
    for (auto e:d) std::cout << "double: " << e << "\n";
}

打印

int: 1
int: 2
int: 3
int: 4
int: 5
double: 6
double: 7
double: 8
double: 9

因此,您基本上可以使用',' 作为船长来编写模板函数。不过我更喜欢operator% 变体。

简单拍摄

template<typename Container>
void MyParse(std::string const& line, Container& container)
{
    auto f(line.begin()), l(line.end());
    bool ok = qi::phrase_parse(
            f, l,
            qi::auto_ % ',', qi::blank, container);

    if (!ok || (f!=l))
        throw "parser error: '" + std::string(f,l) + "'"; // FIXME
} 

变体 2

template<typename Container>
void MyParse(std::string const& line, Container& container)
{
    auto f(line.begin()), l(line.end());
    bool ok = qi::phrase_parse(
            f, l,
            qi::auto_, qi::blank | ',', container);

    if (!ok || (f!=l))
        throw "parser error: '" + std::string(f,l) + "'"; // FIXME
} 

解决string 案例(及其他):

如果元素类型不能被 Spirit '演绎'(任何东西都可以解析成字符串),只需使用一个知道如何解析元素类型的可选解析器/语法?

template<typename Container, typename ElementParser = qi::auto_type>
void MyParse(std::string const& line, Container& container, ElementParser const& elementParser = ElementParser())
{
    auto f(line.begin()), l(line.end());
    bool ok = qi::phrase_parse(
            f, l,
            elementParser % ",", qi::blank, container);

    if (!ok || (f!=l))
        throw "parser error: '" + std::string(f,l) + "'"; // FIXME
} 

现在,它可以很好地解析字符串了:

std::vector<int> i;
std::set<std::string> s;

MyParse("1,22,33,44,15", i);
MyParse("1,22,33,44,15", s, *~qi::char_(","));

for(auto e:i) std::cout << "i: " << e << "\n";
for(auto e:s) std::cout << "s: " << e << "\n";

打印

i: 1
i: 22
i: 33
i: 44
i: 15
s: 1
s: 15
s: 22
s: 33
s: 44

完整列表

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <iostream>

namespace qi = boost::spirit::qi;

    template<typename Container, typename ElementParser = qi::auto_type>
    void MyParse(std::string const& line, Container& container, ElementParser const& elementParser = ElementParser())
    {
        auto f(line.begin()), l(line.end());
        bool ok = qi::phrase_parse(
                f, l,
                elementParser % ",", qi::blank, container);

        if (!ok || (f!=l))
            throw "parser error: '" + std::string(f,l) + "'"; // FIXME
    } 

#include <set>

int main()
{
    std::vector<int> i;
    std::set<std::string> s;

    MyParse("1,22,33,44,15", i);
    MyParse("1,22,33,44,15", s, *~qi::char_(","));

    for(auto e:i) std::cout << "i: " << e << "\n";
    for(auto e:s) std::cout << "s: " << e << "\n";
}

【讨论】:

  • 已更新以处理非auto_-able 元素类型
  • 非常好的方法,我希望我能再给你一个 +1。如果您认为值得(我认为不值得),请随意添加 create_parser 方法。
  • @cv_and_he 我认为提及它非常好(所有这些auto_ 业务毕竟不是魔术)。但是,我认为对于特定于解析器的行为,肯定存在专门的问题。外部链接会导致 ODR 违规。我对法律标准不太了解,但我通常会尽量避免大量的预防措施
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-13
  • 1970-01-01
  • 2012-01-17
  • 1970-01-01
  • 2021-06-06
  • 2011-09-14
  • 2020-08-03
相关资源
最近更新 更多