Próbuję się nauczyć Boost.Spirit, ale napotkam na trudności.Boost.Spirit.x3 unikaj zwijania dwóch kolejnych atrybutów tego samego typu do wektora
Próbuję analizować ciąg znaków w następującej strukturze:
struct employee {
std::string name;
std::string location;
};
I wydaje się, że gdy dwa atrybuty tego samego typu są z powrotem do tyłu, zapadają się (logicznie) do std::vector
tego typu . Z powodu tej zasady, następujące parsera
+x3::ascii::alnum >>
+x3::space >>
+x3::ascii::alnum
miałby atrybut std::vector<std::string>
.
Ale próbuję sparsować to do tego struct
, co oznacza, że idealnym atrybutem dla mnie będzie boost::fusion::tuple<std::string, std::string>
, więc mogę dostosować moją strukturę do niego.
Pełna wersja kodu nie działa (mowa powyżej):
// 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;
}
Ten kod wyzwala static_assert
mówi mi, że mój atrybut nie jest poprawna:
error: static_assert failed "Attribute does not have the expected size."
Na polecenie
clang++ -std=c++14 test.cpp
(to również nie działa zgodnie z GCC).
Co Próbowałem
Znalazłem obejście tego problemu, ale jest bałagan, a ja nie mogę uwierzyć, że jest to najczystszy sposób:
// 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;
}
Naprawdę nie lubię tego rozwiązania: to niszczy niesamowitą ekspresyjność ducha i sprawia, że jest to bardzo brzydkie, także jeśli chcę dodać nowe pola do struktury employee
, to muszę dodać dodatkową lambdę, zamiast tylko aktualizować mój BOOST_FUSION_ADAPT_STRUCT
, co jest znacznie łatwiejsze.
Pytanie brzmi: czy istnieje sposób, aby (miejmy nadzieję) oczyścić dwa kolejne atrybuty tego samego rodzaju z std::vector
i na boost::fusion::vector
?
Z góry dziękuję za daleko;).
To dobry dzień, gdy odpowiedź na pytanie duchów już tam zanim znajdę pytanie :) +1 – sehe
@sehe: Hah! To jest pierwsze, z pewnością pokonałem cię na X3, ale to było bardzo łatwe. ; -] – ildjarn
@Russel Proponuję użyć szypra zamiast wyraźnych białych znaków (zobacz http://stackoverflow.com/questions/17072987/boost-spirit-skipper-issues/17073965#17073965). [Live on Coliru] (http://coliru.stacked-crooked.com/a/444dad3ed2859a35). – sehe