Sto cercando di imparare Boost.Spirit, ma ho trovato una difficoltà.Boost.Spirit.x3 evitare di comprimere due attributi consecutivi dello stesso tipo in un vettore
Sto cercando di analizzare una stringa nella seguente struttura:
struct employee {
std::string name;
std::string location;
};
e sembra che quando due attributi con lo stesso tipo sono back to back, collassano (logicamente) in un std::vector
di quel tipo . A causa di questa regola, il seguente parser
+x3::ascii::alnum >>
+x3::space >>
+x3::ascii::alnum
avrebbe l'attributo di std::vector<std::string>
.
Ma sto cercando di analizzare questo in quello struct
, il che significa che l'attributo ideale per me sarebbe un boost::fusion::tuple<std::string, std::string>
, quindi posso adattare la mia struttura ad esso.
La versione completa del codice non lavora (di cui sopra):
// 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;
}
Questo codice attiva un static_assert
dicendomi che il mio attributo non è corretto:
error: static_assert failed "Attribute does not have the expected size."
Con il comando di
clang++ -std=c++14 test.cpp
(fallisce anche in GCC).
Quello che ho cercato
Ho trovato una soluzione a questo problema, ma è disordinato, e non riesco a credere che questo è il modo più pulito:
// 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;
}
Non mi piace davvero questa soluzione: rovina la sorprendente espressività dello spirito e la rende super-brutta, anche se voglio aggiungere nuovi campi nella struttura employee
, quindi devo aggiungere un lambda in più, invece di aggiornare il mio BOOST_FUSION_ADAPT_STRUCT
, che è molto più facile.
Quindi la domanda è: c'è un modo per (si spera) dividere in modo pulito due attributi consecutivi dello stesso tipo da std::vector
e in un boost::fusion::vector
?
Grazie in anticipo per arrivare così lontano;).
E 'una buona giornata in cui la risposta ad una domanda spirito è già lì prima di trovare la domanda :) +1 – sehe
@sehe: Hah! Questo è il primo che ti ho battuto su una domanda X3 sicuramente, ma poi, questo è stato molto facile. ; -] – ildjarn
@Russel Suggerirei di usare uno skipper invece di uno spazio bianco esplicito (consultate http://stackoverflow.com/questions/17072987/boost-spirit-skipper-issues/17073965#17073965). [Live on Coliru] (http://coliru.stacked-crooked.com/a/444dad3ed2859a35). – sehe