Как передать итератор функции в духе ци

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 или чего-то не уверен, как это сделать), пока методу итераторы передаются с их текущим состоянием.


person vishal129    schedule 24.07.2012    source источник
comment
Любая помощь будет принята с благодарностью, так как мне срочно нужно ее решение.   -  person vishal129    schedule 25.07.2012
comment
Печально то, что они работают ТАК, что для нас не важно, когда вы торопитесь. В следующий раз вы захотите сделать его SSCCE и немного объясните, почему / чего вы пытаетесь достичь (вместо того, чтобы просто понять, как вы этого добьетесь). Это устраняет два препятствия для потенциальных респондентов. (Я старательно отвечаю на большинство вопросов Spirit, но даже мне не всегда нужно время, чтобы выяснить, какие 6 файлов заголовков вы забыли перечислить для меня, и какие пространства имен вы использовали и т. Д.)   -  person sehe    schedule 26.07.2012
comment
@sehe: очень сожалею о файлах заголовков, а также о пространстве имен, при вставке кода произошла ошибка. Запомним это в следующий раз, а также отредактируем приведенный выше код. Произошло это потому, что я не хотел вставлять сюда огромное количество кода.   -  person vishal129    schedule 26.07.2012


Ответы (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/8a874ef32ebcfa8eebfa271127f4d202

Обратите внимание, как это


Полный код

Вот полный код для использования в будущем:

#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";
}
person sehe    schedule 25.07.2012
comment
Почему мне нужно было это сделать, потому что мне нужна была позиция во время синтаксического анализа и сохранения ее на карте (в методе doStuff). И после завершения синтаксического анализа хотите заменить все те на какое-то другое значение. Надеюсь, что это проясняет. Кроме того, большое спасибо за такой подробный ответ, позвольте мне подробно изучить его и задам вопросы, если таковые имеются. Еще раз спасибо. - person vishal129; 26.07.2012
comment
Это глупый вопрос, но если я хочу, чтобы оба шаблона были в публичном разделе класса, что мне для этого нужно сделать? - person vishal129; 26.07.2012
comment
Шаблон для грамматики анализа структуры, а также шаблон функции для doStuff_ - person vishal129; 26.07.2012
comment
Вы можете использовать отложенный вызываемый объект вместо шаблона функции. См. liveworkspace.org/code/b01c3df8e4cf2a3aca3616d0367cac9c (отредактировано b>) для его живой версии. На самом деле я бы не стал предпочитать это, я не вижу выгоды, но я вижу стоимость. - person sehe; 26.07.2012
comment
И бонусная версия с использованием phoenix::function<> вместо phoenix::bind liveworkspace.org/code/0a03883f3721eb4c764ac165fa699ec предпочитать). Документация ссылка - person sehe; 26.07.2012
comment
Хорошо, большое спасибо за обе версии, позвольте мне разобраться в них и посмотреть, какая из них действительно подходит для моего кода - person vishal129; 26.07.2012
comment
Сильно ли влияет Phoenix :: function ‹› на производительность? Он делает это за меня. Есть ли способ настроить, чтобы улучшить производительность? - person vishal129; 31.07.2012
comment
Также есть более быстрый способ сделать то же самое, если мне просто нужна позиция, имя файла, а также номер строки из итератора позиции. Можно ли это сделать в той же строке или что-то в этом роде, не вызывая функцию и, таким образом, уменьшая количество вызовов функций феникса и т. Д.? - person vishal129; 31.07.2012