c++ - Boost Spirit X3 no puede compilar directivas de repetición con factor variable
boost-spirit boost-spirit-x3 (1)
Estoy tratando de usar la repetición de la directiva Boost Spirit X3 con un factor de repetición que es variable. La idea básica es la de un encabezado + carga útil, donde el encabezado especifica el tamaño de la carga útil. Un ejemplo simple "3 1 2 3" se interpreta como encabezado = 3, datos = {1, 2, 3} (3 enteros).
Solo pude encontrar ejemplos de la documentación de Spirit Qi. Utiliza la referencia de impulso Phoenix para ajustar el factor variable: http://www.boost.org/doc/libs/1_50_0/libs/spirit/doc/html/spirit/qi/reference/directive/repeat.html
std::string str;
int n;
test_parser_attr("/x0bHello World",
char_[phx::ref(n) = _1] >> repeat(phx::ref(n))[char_], str);
std::cout << n << '','' << str << std::endl; // will print "11,Hello World"
Escribí el siguiente ejemplo simple para Spirit x3 sin suerte:
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <string>
#include <iostream>
namespace x3 = boost::spirit::x3;
using x3::uint_;
using x3::int_;
using x3::phrase_parse;
using x3::repeat;
using x3::space;
using std::string;
using std::cout;
using std::endl;
int main( int argc, char **argv )
{
string data("3 1 2 3");
string::iterator begin = data.begin();
string::iterator end = data.end();
unsigned int n = 0;
auto f = [&n]( auto &ctx ) { n = x3::_attr(ctx); };
bool r = phrase_parse( begin, end, uint_[f] >> repeat(boost::phoenix::ref(n))[int_], space );
if ( r && begin == end )
cout << "Parse success!" << endl;
else
cout << "Parse failed, remaining: " << string(begin,end) << endl;
return 0;
}
Compilar el código anterior con boost 1.59.0 y clang ++ (flags: -std = c ++ 14) da lo siguiente:
boost_1_59_0/boost/spirit/home/x3/directive/repeat.hpp:72:47: error: no matching constructor for
initialization of ''proto_child0'' (aka ''boost::reference_wrapper<unsigned int>'')
typename RepeatCountLimit::type i{};
Si codifico la
repeat(3)
lugar de la
repeat(boost::phoenix::ref(n))
funciona correctamente, pero no es una solución posible ya que debería admitir un factor de repetición variable.
La compilación con
repeat(n)
completa con éxito, pero no se puede analizar con el siguiente resultado:
“Parse failed, remaining: 1 2 3"
Mirando el código fuente de
boost/spirit/home/x3/directive/repeat.hpp:72
llama al constructor vacío para el tipo de plantilla
RepeatCountLimit::type
variable
i
y luego lo asigna durante el ciclo for, iterando sobre min y max.
Sin embargo, dado que el tipo es una referencia, debe inicializarse en el constructor, por lo que la compilación falla.
Mirando el código fuente equivalente de la versión anterior de la biblioteca boost / spirit / home / qi / directive / repeat.hpp: 162 se asigna directamente:
typename LoopIter::type i = iter.start();
No estoy seguro de lo que estoy haciendo mal aquí, o si x3 actualmente no admite factores de repetición variables. Agradecería un poco de ayuda para resolver este problema. Gracias.
Por lo que deduzco, leyendo la fuente y la lista de correo, Phoenix no está integrado en X3 en absoluto: la razón es que c ++ 14 hace que la mayor parte sea obsoleta.
Estoy de acuerdo en que esto deja algunos puntos donde Qi solía tener soluciones elegantes, por ejemplo,
eps(DEFERRED_CONDITION)
,
lazy(*RULE_PTR)
(el
truco de Nabialek
) y, de hecho, este caso.
Spirit X3 todavía está en desarrollo, por lo que podríamos ver esto agregado¹
Por ahora, Spirit X3 tiene una instalación generalizada para el contexto con estado.
Básicamente, esto reemplaza a los
locals<>
, en algunos casos argumentos heredados, y también se puede / hacer para / validar el número de elementos en este caso particular:
-
x3::with
²
Así es como puedes usarlo:
with<_n>(std::ref(n))
[ omit[uint_[number] ] >>
*(eps [more] >> int_) >> eps [done] ]
Aquí,
_n
es un tipo de etiqueta que identifica el elemento de contexto para la recuperación con
get<_n>(cxtx)
.
Tenga en cuenta que actualmente tenemos que usar un contenedor de referencia para un valor
n
porquewith<_n>(0u)
daría como resultado un elemento constante dentro del contexto. Supongo que esto también es una QoI que se puede levantar a medida que X # madura
Ahora, para las acciones semánticas:
unsigned n;
struct _n{};
auto number = [](auto &ctx) { get<_n>(ctx).get() = _attr(ctx); };
Esto almacena el número sin firmar analizado en el contexto.
(
De hecho, debido al enlace
ref(n)
no es parte del contexto por ahora, como se mencionó
)
auto more = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) > _val(ctx).size(); };
Aquí verificamos que en realidad no estamos "llenos", es decir, se permiten más números enteros
auto done = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) == _val(ctx).size(); };
Aquí comprobamos que estamos "llenos", es decir, no se permiten más números enteros.
Poniendolo todo junto:
#include <string>
#include <iostream>
#include <iomanip>
#include <boost/spirit/home/x3.hpp>
int main() {
for (std::string const input : {
"3 1 2 3", // correct
"4 1 2 3", // too few
"2 1 2 3", // too many
//
" 3 1 2 3 ",
})
{
std::cout << "/nParsing " << std::left << std::setw(20) << ("''" + input + "'':");
std::vector<int> v;
bool ok;
{
using namespace boost::spirit::x3;
unsigned n;
struct _n{};
auto number = [](auto &ctx) { get<_n>(ctx).get() = _attr(ctx); };
auto more = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) > _val(ctx).size(); };
auto done = [](auto &ctx) { _pass(ctx) = get<_n>(ctx) == _val(ctx).size(); };
auto r = rule<struct _r, std::vector<int> > {}
%= with<_n>(std::ref(n))
[ omit[uint_[number] ] >> *(eps [more] >> int_) >> eps [done] ];
ok = phrase_parse(input.begin(), input.end(), r >> eoi, space, v);
}
if (ok) {
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout << v.size() << " elements: ", " "));
} else {
std::cout << "Parse failed";
}
}
}
Que imprime:
Parsing ''3 1 2 3'': 3 elements: 1 2 3
Parsing ''4 1 2 3'': Parse failed
Parsing ''2 1 2 3'': Parse failed
Parsing '' 3 1 2 3 '': 3 elements: 1 2 3
¹ presta tu apoyo / voz en la lista de correo [espíritu general] :)
² no puede encontrar un enlace de documentación adecuado, pero se usa en algunas de las muestras