Problema de propagación del atributo Spirit Qi con la estructura de miembro único

Tengo un problema de comstackción con Spirit Qi en el que se queja de que value_type no es miembro de identificador . Por alguna razón, el sistema de atributos de Qi considera que el identificador es un tipo de contenedor e intenta enumerar su tipo de valor.

Este es un problema similar al de esta pregunta , sin embargo, creo que la causa es la estructura de miembro único y puede estar relacionada con este error .

#include  #include  #include  using namespace boost::spirit::qi; struct identifier { std::wstring name; }; struct problem { identifier _1; identifier _2; identifier _3; }; BOOST_FUSION_ADAPT_STRUCT( identifier, (std::wstring, name) ) BOOST_FUSION_ADAPT_STRUCT( problem, (identifier, _1) (identifier, _2) (identifier, _3) ) int main(int argc, char* argv[]) { rule gr_identifier = eps >> raw[lexeme[(alpha | '_') >> *(alnum | '_')]]; // Ok, compiles /*rule gr_problem = gr_identifier >> gr_identifier >> '(' >> gr_identifier >> ')';*/ // Fails rule gr_problem = gr_identifier >> gr_identifier >> '(' > gr_identifier > ')'; std::wstring input = L"foo goo(hoo)"; /*bool dummy = phrase_parse( input.begin(), input.end(), gr_problem, space);*/ return EXIT_SUCCESS; } 

Curiosamente, esto solo ocurre cuando se usa un analizador de expectativas (ver definición 2 en el ejemplo). La definición 1, usando solo el analizador de secuencia, comstack (y ejecuta) apropiadamente.

¿Alguien sabe la solución adecuada para esto?

Por favor, también mira el ejemplo en vivo

Este es un caso marginal bastante infame en Spirit. El problema es que el manejo de casos especiales de Fusion Sequences de un solo elemento en Spirit rompe algunas abstracciones.

La solución habitual es adaptar el lado del atributo expuesto para que sea menos trivial:

 rule r = eps >> XXX; // the `eps` is there to break the spell 

Sin embargo, aquí eso no funcionará, porque su subexpresión (a > XXX > b) da como resultado otro vector1 y esta vez, ninguna cantidad de paréntesis inteligente o eps -ing lo salvará. [1]

Para abreviar, tengo tres soluciones:


1. #define KEEP_STRING_WORKAROUND

Véalo en vivo en Coliru

En el que simplemente permitirá que gr_identifier devuelva un std::wstring [2] :

 rule gr_identifier = (alpha | '_') >> *(alnum | '_'); 

Esto simplemente pospone la transformación del atributo mágico que usa la adaptación Fusion si es identifier , y por lo tanto rompe el hechizo:

 rule gr_problem = gr_identifier >> gr_identifier >> ('(' > gr_identifier > ')') ; 

Solo funciona. Creo que esta es probablemente la solución menos intrusiva


2. #define DUMMY_WORKAROUND

Véalo en vivo en Coliru

Mediante el cual se desencuaderna el magix … haciendo que el identifier struct no se fusione, se adapte a una secuencia de fusión de un solo elemento. Sí. Esto implica el EvilHack ™ de agregar un campo ficticio. Para minimizar la confusión, vamos a hacer que sea qi::unused_type :

 struct identifier { std::string name; qi::unused_type dummy; }; BOOST_FUSION_ADAPT_STRUCT( identifier, (std::string, name) (qi::unused_type, dummy) ) 

Y ahora:

 rule gr_identifier = (alpha | '_') >> *(alnum | '_') >> attr(42); // that's hacky 

Trabajos


3. #define NO_ADAPT_WORKAROUND

Véalo en vivo en Coliru

La solución final podría ser la más obvia: no adaptar la estructura como una secuencia de fusión en primer lugar, y obtener beneficios:

 struct identifier { std::string name; identifier() = default; explicit identifier(std::string name) : name(std::move(name)) {} }; 

Tenga en cuenta que para permitir la propagación de atributos, ahora necesitará constructores de conversión adecuados para estar presente. Además, se requiere el constructor predeterminado para los atributos expuestos en Spirit.

Ahora,

 rule gr_identifier = as_string [ (alpha | '_') >> *(alnum | '_') ]; // cleaner... but no fusion 

trabajos. Esto podría ser más intuitivo si no necesita Fusion del tipo para otros fines.

Nota: esta variante bien podría ser la más eficiente en tiempo de comstackción

Resumen

Creo que para su código, hay 2 soluciones perfectamente viables (n. ° 1 y n. ° 3), y una menos que estelar (la del campo ficticio), pero la incluí con fines documentales.

Código completo

Para futura referencia

 #define BOOST_SPIRIT_DEBUG #include  #include  #include  namespace qi = boost::spirit::qi; ////////////////////////////////////////// // Select workaround to demonstrate #define KEEP_STRING_WORKAROUND // #define DUMMY_WORKAROUND™ // #define NO_ADAPT_WORKAROUND ////////////////////////////////////////// #if defined(KEEP_STRING_WORKAROUND) struct identifier { std::string name; }; BOOST_FUSION_ADAPT_STRUCT( identifier, (std::string, name) ) #elif defined(DUMMY_WORKAROUND) struct identifier { std::string name; qi::unused_type dummy; }; BOOST_FUSION_ADAPT_STRUCT( identifier, (std::string, name) (qi::unused_type, dummy) ) #elif defined(NO_ADAPT_WORKAROUND) struct identifier { std::string name; identifier() = default; explicit identifier(std::string name) : name(std::move(name)) {} }; #endif struct problem { identifier _1; identifier _2; identifier _3; }; BOOST_FUSION_ADAPT_STRUCT( problem, (identifier, _1) (identifier, _2) (identifier, _3) ) ////////////////////////////////////////// // For BOOST_SPIRIT_DEBUG only: static inline std::ostream& operator<<(std::ostream& os, identifier const& id) { return os << id.name; } ////////////////////////////////////////// int main() { using namespace qi; typedef std::string::const_iterator It; #if defined(KEEP_STRING_WORKAROUND) rule gr_identifier = (alpha | '_') >> *(alnum | '_'); #elif defined(DUMMY_WORKAROUND) rule gr_identifier = (alpha | '_') >> *(alnum | '_') >> attr(42); // that's hacky #elif defined(NO_ADAPT_WORKAROUND) rule gr_identifier = as_string [ (alpha | '_') >> *(alnum | '_') ]; // cleaner... but no fusion #endif rule gr_problem = gr_identifier >> gr_identifier >> ('(' > gr_identifier > ')') ; std::string input = "foo goo(hoo)"; BOOST_SPIRIT_DEBUG_NODES((gr_problem)(gr_identifier)); It f(begin(input)), l(end(input)); bool dummy = phrase_parse(f, l, gr_problem, qi::space); return dummy? 0 : 255; } 

[1] Créame, lo intenté, incluso cuando insertaba qi::unused_type “fake” y / o usando attr_cast<> o reglas de ayuda para forzar el tipo de subexpresión.

[2] Para fines de demostración, utilicé std::string porque creo que se mezcla mejor con BOOST_SPIRIT_DEBUG

    Intereting Posts