boost::spirit дважды использует один и тот же символ

#c #boost #boost-spirit

#c #boost #boost-spirit

Вопрос:

У меня есть грамматика, похожая на эту:

  template <class ITER>
struct MessageParser
    : public boost::spirit::qi::grammar<ITER, Message(), boost::spirit::ascii::space_type>
{
    MessageParser()
        : MessageParser::base_type(start_)
    {
        string_ =  (char_("a-zA-Z_") >> *char_("a-zA-Z_0-9"));
        quoted_string_ = lexeme['"' >>  (char_ - '"') >> '"'];
        signal_ %= lit("SG_")  // type is Signal
            >> string_ >> ':'; // Signal name
        message_ %= lit("BO_") // type is Message
            >> int_
            >> string_ >> ':'
            >>  signal_; // std::map<std::string, Signal> (here is my problem)
        start_ %= message_;
    }
    boost::spirit::qi::rule<ITER, Message(), boost::spirit::ascii::space_type> start_;
    boost::spirit::qi::rule<ITER, Message(), boost::spirit::ascii::space_type> message_;
    boost::spirit::qi::rule<ITER, Signal(), boost::spirit::ascii::space_type> signal_;
    boost::spirit::qi::rule<ITER, std::string()> string_;
    boost::spirit::qi::rule<ITER, std::string(), boost::spirit::ascii::space_type> quoted_string_;
};
  

Проблема в том, что мне нужно имя Signal для создания Signal объекта (потому что у него есть name атрибут), но я также хочу сопоставить сигнал, проанализированный с помощью правила signal_ , с именем сигнала на std::map<std::string, Signal> карте сообщений, но я не знаю, как получить имя сигнала или как создать необходимую пару для boost::spirit , чтобы он мог вставить пару в карту.
Я думаю, что мне нужно каким-то образом дублировать строку, содержащую имя сигнала.

Как я могу это сделать?

Комментарии:

1. Это выглядит точно так же, как проблема, которую symbols решает анализатор, но на самом деле вы можете достичь этого несколькими различными способами: преобразование либо с помощью правила, либо attr_cast , семантическое действие, пользовательский анализатор и, возможно, я забыл какой-то другой.

2. Не могли бы вы объяснить свой подход более подробно? Я думал о symbols подобном mapper, который я могу использовать для сопоставления проанализированного целого числа с типом enum… Как я могу использовать это, чтобы получить значение проанализированного имени другого правила? Другие перечисленные вами вещи я пока не знаю. Но спасибо за отправную точку.

3. @NikitaKniazev Я предполагаю, что я пытался преобразовать с помощью семантического действия, но затем я столкнулся с проблемой, что я не знаю, как получить std::map<std::string, Signal> контейнер в моей пользовательской функции, у меня там есть только мой Signalamp; .

4. Я решил это с помощью attr_cast и transform_attribute , так что большое спасибо.

5. 1) Он может сопоставляться с любым типом, а не только с перечислением, которое даже используется в примере в документации int . 2) Вам действительно нужно строковое представление? Даже если вам действительно нужно, вы можете захватить строку, которую анализатор символов сопоставил с raw директивой raw[symbols[assign_mapped_value]][assign_matched_string] . 3) Вы можете передать его через правила, унаследованные атрибуты (аргументы)

Ответ №1:

Я решил это с помощью attr_cast :

 namespace boost
{
    namespace spirit
    {
        namespace traits
        {
            template <>
            struct transform_attribute<std::pair<std::string, Signal>, Signal, boost::spirit::qi::domain>
            {
                using type = Signalamp;;
                using pair_t = std::pair<std::string, Signal>;
                static Signalamp; pre(pair_tamp; pair)
                {
                    return pair.second;
                }
                static void post(pair_tamp; pair, const Signalamp; sig)
                {
                    pair.first = sig.name;
                    pair.second = sig;
                }
                static void fail(pair_tamp; pair)
                {
                }
            };
        }
    }
}
  

И затем:

 message_ %= lit("BO_")
    >> int_
    >> string_ >> ':'
    >> int_
    >> string_
    >>  attr_cast(signal_);
  

Надеюсь, я использовал все правильно (без гарантии).