Boost.Spirit.x3 позволяет избежать свертывания двух последовательных атрибутов одного типа в вектор

Я пытаюсь выучить Boost.Spirit, но я нашел трудность.

Я пытаюсь разобрать строку в следующую структуру:

struct employee {
std::string name;
std::string location;
};

И кажется, что когда два атрибута с одним и тем же типом идут вплотную, они сворачиваются (логически) в std::vector этого типа. Из-за этого правила следующий парсер

+x3::ascii::alnum >>
+x3::space >>
+x3::ascii::alnum

будет иметь атрибут std::vector<std::string>,

Но я пытаюсь разобраться в этом struct, что означает, что идеальным атрибутом для меня будет boost::fusion::tuple<std::string, std::string>так что я могу адаптировать мою структуру к нему.

Полная версия неработающего кода (ссылка выше):

// Example program
#include <iostream>
#include <string>

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>struct employee {
std::string name;
std::string location;
};

BOOST_FUSION_ADAPT_STRUCT(employee,
(std::string, name),
(std::string, location)
)

namespace x3 = boost::spirit::x3;

x3::rule<struct parse_emp_id, employee> const parse_emp = "Employee Parser";
auto parse_emp_def =
+x3::ascii::alnum >>
+x3::space >>
+x3::ascii::alnum
;
BOOST_SPIRIT_DEFINE(parse_emp);

int main()
{
std::string input = "Joe Fairbanks";

employee ret;

x3::parse(input.begin(), input.end(), parse_emp, ret);

std::cout << "Name: " << ret.name << "\tLocation: " << ret.location << std::endl;
}

Смотрите это в прямом эфире

Этот код вызывает static_assert говорит мне, что мой атрибут неверен:

error: static_assert failed "Attribute does not have the expected size."

С командой

clang++ -std=c++14 test.cpp

(это также терпит неудачу под GCC).

Я нашел решение этой проблемы, но это грязно, и я не могу поверить, что это самый чистый способ:

// Example program
#include <iostream>
#include <string>

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>struct employee {
std::string name;
std::string location;
};

namespace x3 = boost::spirit::x3;

x3::rule<struct parse_emp_id, employee> const parse_emp = "Employee Parser";
auto parse_emp_def =
x3::eps [
([](auto& ctx) {
x3::_val(ctx) = employee{};
})
]>>
(+x3::ascii::alnum)[
([](auto& ctx) {
x3::_val(ctx).name = x3::_attr(ctx);
})
]>>
+x3::space >>
(+x3::ascii::alnum)[
([](auto& ctx) {
x3::_val(ctx).location = x3::_attr(ctx);
})
]
;
BOOST_SPIRIT_DEFINE(parse_emp);

int main()
{
std::string input = "Joe Fairbanks";

employee ret;

x3::parse(input.begin(), input.end(), parse_emp, ret);

std::cout << "Name: " << ret.name << "\tLocation: " << ret.location << std::endl;
}

Смотрите это в прямом эфире

Мне действительно не нравится это решение: оно как бы разрушает удивительную выразительность духа и делает его безобразным, даже если я хочу добавить новые поля в employee struct, тогда я должен добавить дополнительную лямбду, вместо того, чтобы просто обновить мой BOOST_FUSION_ADAPT_STRUCT, что намного проще.

Таким образом, вопрос заключается в следующем: есть ли способ (надеюсь) аккуратно разделить два последовательных атрибута одного типа из std::vector и в boost::fusion::vector?

Заранее спасибо за то, что зашли так далеко;).

9

Решение

Проблема в том, что в отличие от литералов символов, x3::space имеет атрибут. Таким образом, у вас нет атрибута двух отдельных последовательностей символов, разделенных пробелами, а скорее атрибута одной большой последовательности символов, которая включает пробелы.

omit директива это то, что вам нужно, и с этим единственным дополнением работает ваш «неработающий код». : -]

// Example program
#include <string>
#include <iostream>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/home/x3.hpp>

namespace x3 = boost::spirit::x3;

struct employee {
std::string name;
std::string location;
};
BOOST_FUSION_ADAPT_STRUCT(employee, name, location)

x3::rule<struct parse_emp_id, employee> const parse_emp = "Employee Parser";
auto parse_emp_def
=      +x3::ascii::alnum
>>  x3::omit[+x3::space]
>> +x3::ascii::alnum
;
BOOST_SPIRIT_DEFINE(parse_emp)

int main()
{
std::string const input = "Joe Fairbanks";

employee ret;
x3::parse(input.begin(), input.end(), parse_emp, ret);

std::cout << "Name: " << ret.name << "\tLocation: " << ret.location << '\n';
}

Демо онлайн

6

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector