#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_);
Надеюсь, я использовал все правильно (без гарантии).