【问题标题】:How to pass the iterator to a function in spirit qi如何将迭代器传递给灵气中的函数
【发布时间】:2012-07-23 04:23:42
【问题描述】:
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;
}

我有上面的示例代码(此处使用的代码来自 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 或不确定如何做到这一点)。

【问题讨论】:

  • 任何帮助将不胜感激,因为我急需解决方案。
  • 可悲的是,他们的方式如此有效,当您赶时间时,这对我们来说并不重要。下一次,您将希望将其设为SSCCE,并解释一下您为什么/尝试实现(而不是仅仅解释您认为您将如何实现它)。这消除了潜在回答者的两个关闭。 (我很努力地回答大多数 Spirit 问题,但即使我也不必总是有时间找出你忘记为我列出的 6 个头文件,以及你使用了哪些命名空间等)
  • @sehe :对于头文件和命名空间真的很抱歉,是粘贴代码的错误。下次会记住它,也会编辑上面的代码。发生这种情况是因为我不想在这里粘贴大量代码。

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


【解决方案1】:

我不完全确定您为什么认为您“需要”您所描述的内容。恐怕您的实际任务的解决方案可能非常简单:

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

但是,由于实际问题非常有趣,并且将位置插入器与istream_buf(而不是通常的(较慢)boost::spirit::istream_iterator)结合使用有其优点,我将向您展示如何做它也具有语义动作。

对于一个简单(但相当完整)的测试主要

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";
}

带有语义动作的输出现在变成:

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)

直播 http://liveworkspace.org/code/8a874ef3...

注意它是怎么回事

  • 展示了对实际解析器实例名称('start_p')的访问权限
  • 展示了对完整源迭代器范围的访问
  • 展示了如何在语义动作中进行专门的处理
  • 我仍然建议使用qi::double_ 来解析原始输入,因为这是我所知道的唯一可以轻松处理所有情况的东西(请参阅测试数据和其他问题:Is it possible to read infinity or NaN values using input streams?
  • 通过显示解析值的统计信息,高效演示将实际数据解析到向量中

完整代码

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

#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";
}

【讨论】:

  • 为什么我需要这样做是因为我在解析时需要位置并将其保存在地图中(在 doStuff 方法中)。解析完成后,希望将所有这些替换为其他值。希望这说明清楚。另外,非常感谢您提供如此详细的答案,让我正确地完成它,如果有疑问会提出疑问。再次感谢。
  • 这是一个愚蠢的问题,但如果我想要课堂公共部分的两个模板,我该怎么办?
  • struct parse语法的模板以及doStuff_的函数模板
  • 您可以使用延迟的可调用对象而不是函数模板。查看 liveworkspace.org/code/b01c3df8e4cf2a3aca3616d0367cac9c (edited) 了解它的实时版本。我实际上并不喜欢它,我没有看到收益,但我确实看到了成本。
  • 还有一个使用phoenix::function&lt;&gt; 而不是phoenix::bind liveworkspace.org/code/0a03883f3721eb4c764ac165a699ec9f 的奖励版本(我非常喜欢)。文档link
猜你喜欢
  • 1970-01-01
  • 2015-04-15
  • 2013-06-03
  • 2019-12-10
  • 2019-12-23
  • 1970-01-01
  • 2012-07-11
  • 2014-06-26
  • 2013-09-02
相关资源
最近更新 更多