2016-05-10 11 views
9

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; 
} 

See it live

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; 
} 

See it live

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;).

Odpowiedz

6

Problem polega na tym, że w przeciwieństwie do literałów znaków, x3::space ma atrybut. Więc nie masz atrybutu dwóch oddzielnych sekwencji znaków oddzielonych białymi znakami, ale raczej atrybut jednej dużej sekwencji znaków, która zawiera białe spacje.

The omit directive to jest to, czego szukasz, a dzięki jednemu dodatkowi działa Twój "nie działający kod".: -]

// 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'; 
} 

Online Demo

+2

To dobry dzień, gdy odpowiedź na pytanie duchów już tam zanim znajdę pytanie :) +1 – sehe

+0

@sehe: Hah! To jest pierwsze, z pewnością pokonałem cię na X3, ale to było bardzo łatwe. ; -] – ildjarn

+1

@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

Powiązane problemy