如何将迭代器传递给spirit qi中的函数 [英] How to pass the iterator to a function in spirit qi

查看:139
本文介绍了如何将迭代器传递给spirit qi中的函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  template< typename Iterator> 
struct parse_grammar
:qi :: grammar< Iterator,std :: string()>
{
parse_grammar()
:parse_grammar :: base_type(start_p,start_p){
a_p =','& qi :: double_;
b_p = * a_p;
start_p = qi :: double_> b_p>> qi :: eoi;
}

qi :: rule< Iterator,std :: string()> a_p;
qi :: rule< Iterator,std :: string()> b_p;
qi :: rule< Iterator,std :: string()> start_p;
};


//实现

std :: vector< double> parse(std :: istream& input,const std :: string& filename)

{

//迭代流输入

typedef std :: istreambuf_iterator< char> base_iterator_type;
base_iterator_type in_begin(input);

//将输入迭代器转换为向前迭代器,由精神解析器使用
typedef boost :: spirit :: multi_pass< base_iterator_type> forward_iterator_type;
forward_iterator_type fwd_begin = boost :: spirit :: make_default_multi_pass(in_begin);
forward_iterator_type fwd_end;

//准备输出
std :: vector< double>输出;
//用位置迭代器包装前向迭代器,记录位置
typedef classic :: position_iterator2< forward_iterator_type> pos_iterator_type;
pos_iterator_type position_begin(fwd_begin,fwd_end,filename);
pos_iterator_type position_end

parse_grammar< pos_iterator_type>公克;

// parse
try
{
qi :: phrase_parse(
position_begin,position_end,// iterators over input
gram, /识别双精度列表
ascii :: space); // comment skipper
}
catch(const qi :: expectation_failure< pos_iterator_type>& e)
{
const classic :: file_position_base< std :: string> pos = e.first.get_position();
std :: stringstream msg;
msg<<
文件中解析错误< pos.file<
line<< pos.line<< column<< pos.column<< std :: endl<
'<< e.first.get_currentline()<< '< std :: endl<
<< ^ - here;
throw std :: runtime_error(msg.str());
}

//返回结果
返回输出;
}



我有以上示例代码(代码来自boost-spirit网站



在规则a_p的语法中我想使用语义动作并调用一个方法,并将迭代器传递给它如下:

  a_p =','> qi :: double_ [boost :: bind(& parse_grammar :: doStuff(),this,
boost :: ref(position_begin),boost :: ref(position_end)];

,如果方法doStuff的签名如下:

  void doStuff(pos_iterator_type const& first,pos_iterator_type const& last); 

任何想法如何做到这一点
我不介意任何方式(如果我可以使用boost :: phoenix或一些不知道如何)只要方法迭代器传递与他们的当前状态。 / p>

解决方案

我不完全确定为什么你认为你需要'你描述的内容恐怕你的实际解决方案任务可能很简单:

  start_p = qi :: double_%','> qi :: eoi; 

然而,由于实际的问题很有趣,并且使用位置中继器与 istream_buf (而不是通常的(慢) boost :: spirit :: istream_iterator )有它的优点,我会告诉你如何



对于

的一个简单的(但相当完整的)测试主体

  int main()
{
std :: istringstream iss(
1,-3.4,3.1415926 \\\

inF,-NaN,\\\

2, - 。4,4.14e7 \\\
);

data_t parsed = parse(iss,< inline-test>);

std :: cout<< 完成,解析< parsed.size()<< values(
<<min:<< * std :: min_element(parsed.begin(),parsed.end())<<,
< ;<max:<< * std :: max_element(parsed.begin(),parsed.end())<<)\\\
;
}

使用语义操作输出现在变成:

 在< inline-test>时调试('start_p'):1:[1..2]'1'= 1 
< inline-test>中的debug('start_p'):1:[4..8]'-3.4'= -3.4
< inline-test> ;:1:[10..19]'3.1415926'= 3.14159
< inline-test>时的debug('start_p'):2:[2..6]'+ inF'= inf $ b $ 2:[7..11]'-NaN'= -nan
在< inline-test>时的调试('start_p'):3: [1..2]'2'= 2
在< inline-test>下的debug('start_p'):3:[3..6]'-.4'= -0.4
debug ('start_p')at :3:[7..13]'4.14e7'= 4.14e + 07
完成,解析8个值(最小值:-3.4,最大值:inf)



em> http://liveworkspace.org/code/8a874ef3 ...



注意如何




  • 演示访问实际解析器的名称

  • 显示如何在语义操作内进行专门处理

  • <
  • 我仍然建议使用 qi :: double _ 来解析原始输入,因为它是我知道的唯一的轻松处理所有情况(请参阅测试数据和此其他问题:是否可以使用输入流读取无穷大或NaN值?

  • 演示了将实际数据有效地解析到向量中通过显示已解析值的统计信息 > >

    这是以后参考的完整代码:

      #define BOOST_SPIRIT_USE_PHOENIX_V3 
    #include< boost / spirit / include / qi.hpp>
    #include< boost / spirit / include / phoenix.hpp>
    #include< boost / spirit / include / support_multi_pass.hpp>
    #include< boost / spirit / include / classic_position_iterator.hpp>
    #include< boost / phoenix / function / adapt_function.hpp>

    命名空间qi = boost :: spirit :: qi;
    命名空间phx = boost :: phoenix;
    namespace classic = boost :: spirit :: classic;
    namespace ascii = boost :: spirit :: ascii;

    typedef std :: vector< double> data_t;

    /////////使用自由功能
    //
    模板< typename语法,typename范围>
    double doStuff_(Grammar& grammar,Range pos_range)
    {
    //效率,缓存adhoc语法:
    static const qi :: rule< typename Range :: iterator ,double()> r_double = qi :: double_;
    static const qi :: grammar< typename Range :: iterator,double()> g_double(r_double); //缓存只是规则可能就足够了,实际上

    double value = 0;
    qi :: parse(pos_range.begin(),pos_range.end(),g_double,value);

    std :: cout<< debug('<< grammar.name()<<')at
    < pos_range.begin()。get_position()。file<< :
    < pos_range.begin()。get_position()。line<< :[
    << pos_range.begin()。get_position()。column<< ..
    << pos_range.end().get_position()。column<< ] \t
    << '< std :: string(pos_range.begin(),pos_range.end())< '\t =
    <<值
    << '\\\
    ';

    返回值;
    }

    BOOST_PHOENIX_ADAPT_FUNCTION(double,doStuff,doStuff_,2)

    template< typename Iterator,typename Skipper>
    struct parse_grammar:qi :: grammar< Iterator,data_t(),Skipper>
    {
    parse_grammar()
    :parse_grammar :: base_type(start_p,start_p)
    {
    使用qi :: raw;
    using qi :: double_;
    使用qi :: _ 1;
    使用qi :: _ val;
    使用qi :: eoi;
    using phx :: push_back;

    value_p = raw [double_] [_val = doStuff(phx :: ref(* this),_1)];
    start_p = value_p%','> eoi;

    // //不使用语义操作使用(更高效):
    // start_p = double_%','>> eoi;
    }

    qi :: rule< Iterator,data_t :: value_type(),Skipper> value_p;
    qi :: rule< Iterator,data_t(),Skipper> start_p;
    };

    //实现
    data_t parse(std :: istream& input,const std :: string& filename)
    {
    // $ b typedef std :: istreambuf_iterator< char> base_iterator_type;
    base_iterator_type in_begin(input);

    //将输入迭代器转换为向前迭代器,由精神解析器使用
    typedef boost :: spirit :: multi_pass< base_iterator_type> forward_iterator_type;
    forward_iterator_type fwd_begin = boost :: spirit :: make_default_multi_pass(in_begin);
    forward_iterator_type fwd_end;

    //用位置迭代器包装前向迭代器,记录位置
    typedef classic :: position_iterator2< forward_iterator_type> pos_iterator_type;
    pos_iterator_type position_begin(fwd_begin,fwd_end,filename);
    pos_iterator_type position_end;

    parse_grammar< pos_iterator_type,ascii :: space_type>公克;

    data_t输出;
    // parse
    try
    {
    if(!qi :: phrase_parse(
    position_begin,position_end,// iterators over input
    gram,//识别双精度列表
    ascii :: space,// comment skipper
    output)//< - attribute reference

    {
    std :: cerr< ;& parse failed at
    << position_begin.get_position()。file<< :
    << position_begin.get_position()。line<< :
    << position_begin.get_position()。column<< \\\
    ;
    }
    }
    catch(const qi :: expectation_failure< pos_iterator_type>& e)
    {
    const classic :: file_position_base< std :: string> pos = e.first.get_position();
    std :: stringstream msg;
    msg<< parse error at file< pos.file
    << line<< pos.line
    << column<< pos.column
    << \\\
    \t'< e.first.get_currentline()
    << '\\\
    \t< std :: string(pos.column,'')<< ^ - here;

    throw std :: runtime_error(msg.str());
    }

    返回输出;
    }

    int main()
    {
    std :: istringstream iss(
    1,-3.4,3.1415926\\\

    ,+ inF,-NaN,\ n
    2, - 。4,4.14e7 \\\
    );

    data_t parsed = parse(iss,< inline-test>);

    std :: cout<< 完成,解析< parsed.size()<< values(
    <<min:<< * std :: min_element(parsed.begin(),parsed.end())<<,
    < ;<max:<< * std :: max_element(parsed.begin(),parsed.end())<<)\\\
    ;
    }


    template <typename Iterator>
    struct parse_grammar
    : qi::grammar<Iterator, std::string()>
    {
        parse_grammar()
            : parse_grammar::base_type(start_p, "start_p"){
                a_p = ',' > qi::double_;
                b_p = *a_p;
                start_p = qi::double_ > b_p >> qi::eoi;
            }
    
        qi::rule<Iterator, std::string()> a_p;
        qi::rule<Iterator, std::string()> b_p;
        qi::rule<Iterator, std::string()> start_p;
    };
    
    
    // implementation
    
    std::vector<double> parse(std::istream& input, const std::string& filename)
    
    {
    
    // iterate over stream input
    
        typedef std::istreambuf_iterator<char> base_iterator_type;
        base_iterator_type in_begin(input);
    
        // convert input iterator to forward iterator, usable by spirit parser
        typedef boost::spirit::multi_pass<base_iterator_type> forward_iterator_type;
        forward_iterator_type fwd_begin = boost::spirit::make_default_multi_pass(in_begin);
        forward_iterator_type fwd_end;
    
        // prepare output
        std::vector<double> output;
        // wrap forward iterator with position iterator, to record the position
        typedef classic::position_iterator2<forward_iterator_type> pos_iterator_type;
        pos_iterator_type position_begin(fwd_begin, fwd_end, filename);
        pos_iterator_type position_end;
    
        parse_grammar<pos_iterator_type> gram;
    
        // parse
        try
        {
            qi::phrase_parse(
                    position_begin, position_end,                     // iterators over input
                    gram,                                         // recognize list of doubles
                    ascii::space);                                         // comment skipper
        }
        catch(const qi::expectation_failure<pos_iterator_type>& e)
        {
            const classic::file_position_base<std::string>& pos = e.first.get_position();
            std::stringstream msg;
            msg <<
                "parse error at file " << pos.file <<
                " line " << pos.line << " column " << pos.column << std::endl <<
                "'" << e.first.get_currentline() << "'" << std::endl <<
                " " << "^- here";
            throw std::runtime_error(msg.str());
        }
    
        // return result
        return output;
    }
    

    I have this above sample code(Code used from boost-spirit website for example here).

    In the grammar in the rule a_p I want to use semantic action and call a method and pass the iterator to it something as below:

    a_p = ',' > qi::double_[boost::bind(&parse_grammar::doStuff(), this, 
        boost::ref(position_begin), boost::ref(position_end)];
    

    and if the signature of the method doStuff is like this:

    void doStuff(pos_iterator_type const& first, pos_iterator_type const& last);
    

    Any ideas how to do this? I do not mind any way(if I can do it using boost::phoenix or something not sure how) as long as to the method the iterators are passed with their current state.

    解决方案

    I'm not completely sure why you think you 'need' what you describe. I'm afraid the solution to your actual task might be very simple:

    start_p = qi::double_ % ',' > qi::eoi;
    

    However, since the actual question is quite interesting, and the use of position interators in combination with istream_buf (rather than just the usual (slower) boost::spirit::istream_iterator) has it's merit, I'll show you how to do it with the semantic action as well.

    For a simple (but rather complete) test main of

    int main()
    {
        std::istringstream iss(
                "1, -3.4 ,3.1415926\n"
                ",+inF,-NaN  ,\n"
                "2,-.4,4.14e7\n");
    
        data_t parsed = parse(iss, "<inline-test>");
    
        std::cout << "Done, parsed " << parsed.size() << " values ("
            << "min: " << *std::min_element(parsed.begin(), parsed.end()) << ", "
            << "max: " << *std::max_element(parsed.begin(), parsed.end()) << ")\n";
    }
    

    The output with the semantic action now becomes:

    debug ('start_p') at <inline-test>:1:[1..2] '1'  = 1
    debug ('start_p') at <inline-test>:1:[4..8] '-3.4'   = -3.4
    debug ('start_p') at <inline-test>:1:[10..19]   '3.1415926'  = 3.14159
    debug ('start_p') at <inline-test>:2:[2..6] '+inF'   = inf
    debug ('start_p') at <inline-test>:2:[7..11]    '-NaN'   = -nan
    debug ('start_p') at <inline-test>:3:[1..2] '2'  = 2
    debug ('start_p') at <inline-test>:3:[3..6] '-.4'    = -0.4
    debug ('start_p') at <inline-test>:3:[7..13]    '4.14e7'     = 4.14e+07
    Done, parsed 8 values (min: -3.4, max: inf)
    

    See it live at http://liveworkspace.org/code/8a874ef3...

    Note how it

    • demonstrates access to the name of the actual parser instance ('start_p')
    • demonstrates accces to the full source iterator range
    • shows how to do specialized processing inside the semantic action
    • I still suggest using qi::double_ to parse the raw input, because it is the only thing I know that easily handles all cases (see test data and this other question: Is it possible to read infinity or NaN values using input streams?)
    • demonstrates parsing the actual data into the vector efficiently by displaying statistics of the parsed values

    Full Code

    Here is the full code for future reference:

    #define BOOST_SPIRIT_USE_PHOENIX_V3
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    #include <boost/spirit/include/support_multi_pass.hpp>
    #include <boost/spirit/include/classic_position_iterator.hpp>
    #include <boost/phoenix/function/adapt_function.hpp>
    
    namespace qi      = boost::spirit::qi;
    namespace phx     = boost::phoenix;
    namespace classic = boost::spirit::classic;
    namespace ascii   = boost::spirit::ascii;
    
    typedef std::vector<double> data_t;
    
    ///////// USING A FREE FUNCTION
    //
    template <typename Grammar, typename Range>
        double doStuff_(Grammar &grammar, Range pos_range)
    {
        // for efficiency, cache adhoc grammar:
        static const qi::rule   <typename Range::iterator, double()> r_double = qi::double_;
        static const qi::grammar<typename Range::iterator, double()> g_double(r_double); // caching just the rule may be enough, actually
    
        double value = 0;
        qi::parse(pos_range.begin(), pos_range.end(), g_double, value);
    
        std::cout << "debug ('" << grammar.name() << "') at "
           << pos_range.begin().get_position().file   << ":"
           << pos_range.begin().get_position().line   << ":["
           << pos_range.begin().get_position().column << ".." 
           << pos_range.end  ().get_position().column << "]\t" 
           << "'" << std::string(pos_range.begin(),pos_range.end()) << "'\t = "
           << value
           << '\n';
    
        return value;
    }
    
    BOOST_PHOENIX_ADAPT_FUNCTION(double, doStuff, doStuff_, 2)
    
    template <typename Iterator, typename Skipper>
    struct parse_grammar : qi::grammar<Iterator, data_t(), Skipper>
    {
        parse_grammar()
            : parse_grammar::base_type(start_p, "start_p")
        {
            using qi::raw;
            using qi::double_;
            using qi::_1;
            using qi::_val;
            using qi::eoi;
            using phx::push_back;
    
            value_p = raw [ double_ ] [ _val = doStuff(phx::ref(*this), _1) ];
            start_p = value_p % ',' > eoi;
    
            // // To use without the semantic action (more efficient):
            // start_p = double_ % ',' >> eoi;
        }
    
        qi::rule<Iterator, data_t::value_type(), Skipper> value_p;
        qi::rule<Iterator, data_t(), Skipper> start_p;
    };
    
    // implementation
    data_t parse(std::istream& input, const std::string& filename)
    {
        // iterate over stream input
        typedef std::istreambuf_iterator<char> base_iterator_type;
        base_iterator_type in_begin(input);
    
        // convert input iterator to forward iterator, usable by spirit parser
        typedef boost::spirit::multi_pass<base_iterator_type> forward_iterator_type;
        forward_iterator_type fwd_begin = boost::spirit::make_default_multi_pass(in_begin);
        forward_iterator_type fwd_end;
    
        // wrap forward iterator with position iterator, to record the position
        typedef classic::position_iterator2<forward_iterator_type> pos_iterator_type;
        pos_iterator_type position_begin(fwd_begin, fwd_end, filename);
        pos_iterator_type position_end;
    
        parse_grammar<pos_iterator_type, ascii::space_type> gram;
    
        data_t output;
        // parse
        try
        {
            if (!qi::phrase_parse(
                    position_begin, position_end,  // iterators over input
                    gram,                          // recognize list of doubles
                    ascii::space,                  // comment skipper
                    output)                        // <-- attribute reference
               )
            {
                std::cerr << "Parse failed at " 
                   << position_begin.get_position().file   << ":"
                   << position_begin.get_position().line   << ":"
                   << position_begin.get_position().column << "\n";
            }
        }
        catch(const qi::expectation_failure<pos_iterator_type>& e)
        {
            const classic::file_position_base<std::string>& pos = e.first.get_position();
            std::stringstream msg;
            msg << "parse error at file " << pos.file
                << " line "               << pos.line
                << " column "             << pos.column
                << "\n\t'"                << e.first.get_currentline()
                << "'\n\t "               << std::string(pos.column, ' ') << "^-- here";
    
            throw std::runtime_error(msg.str());
        }
    
        return output;
    }
    
    int main()
    {
        std::istringstream iss(
                "1, -3.4 ,3.1415926\n"
                ",+inF,-NaN  ,\n"
                "2,-.4,4.14e7\n");
    
        data_t parsed = parse(iss, "<inline-test>");
    
        std::cout << "Done, parsed " << parsed.size() << " values ("
            << "min: " << *std::min_element(parsed.begin(), parsed.end()) << ", "
            << "max: " << *std::max_element(parsed.begin(), parsed.end()) << ")\n";
    }
    

    这篇关于如何将迭代器传递给spirit qi中的函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆