2016-08-10 7 views
0

Я пытаюсь создать общие парсер-элементы с использованием qi, поскольку я, к сожалению (MSVC должен быть поддержан), не может использовать X3. Идея заключается в том, чтобы иметь шаблонный-структуру:Как создать общий парсер с помощью qi?

template<class T> struct parse_type; 

который я мог бы использовать так:

template<class T> T from_string(std::string const& s) 
{ 
    T res; 
    parse_type<T> t; 
    ... 
    if (phrase_parse(...,parse_type<T>(),...,t)) 
} 

или специализироваться как этот

template<class T,class Alloc> 
struct parse_type<std::vector<T,Alloc>> 
{ 
    // Parse a vector using rule '[' >> parse_type<T> % ',' > ']'; 
} 

Основной целью является, чтобы позволить легко разбор, например std :: tuple, boost :: optional и boost :: variant (последнее не может быть автоматическим из-за жадного характера qi).

Буду признателен за обратную связь относительно подхода. В настоящее время я основываю свою структуру на qi :: grammar, но грамматика не поддерживается в X3, и я бы хотел использовать X3, когда MSVC компилирует это, и мне также немного неудобно, когда вам нужно предоставить шкипера. Альтернативой может быть статическая функция в parse_type, которая возвращает соответствующее правило. Я думаю, если это более чистый подход?

Любая обратная связь будет оценена по достоинству.

Update2: Замененный фрагмент кода с компилируемым примером, который не работает во время выполнения. Вот код:

#include <boost/spirit/include/qi.hpp> 
#include <boost/spirit/include/phoenix.hpp> 
#include <string> 
#include <string> 
#include <iostream> 
#include <iostream> 

// Support to simplify 
using iter = std::string::const_iterator; 
void print(std::vector<int> const& v) 
{ 
    std::cout << '['; 
    for (auto i: v) std::cout << i << ','; 
    std::cout << "]"; 
} 

namespace qi = boost::spirit::qi; 

// My rule factory - quite useless if you do not specialise 
template<class T> struct ps_rule; 

// An example of using the factory 
template<class T> 
T from_string(std::string const& s) 
{ 
    T result; 
    iter first { std::begin(s) }; 
    auto rule = ps_rule<T>::get(); 
    phrase_parse(first,std::end(s),rule,qi::space,result); 
    return result; 
} 

// Specialising rule for int 
template<> 
struct ps_rule<int> 
{ 
    static qi::rule<iter,int()> get() { return qi::int_; } 
}; 

// ... and for std::vector (where the elements must have rules) 
template<class T,class Alloc> 
struct ps_rule<std::vector<T,Alloc>> 
{ 
    static qi::rule<iter,std::vector<T,Alloc>()> get() 
    { 
     qi::rule<iter,std::vector<T,Alloc>()> res; 
     res.name("Vector"); 
     res = 
       qi::lit('{') 
      >> ps_rule<T>::get() % ',' 
      >> '}'; 
     return res; 
    } 
}; 

int main() 
{ 
    // This one works like a charm. 
    std::cout << ((from_string<int>("100") == 100) ? "OK\n":"Failed\n"); 

    std::vector<int> v {1,2,3,4,5,6}; 

    // This one fails 
    std::cout << ((from_string<std::vector<int>>("{1,2,3,4,5,6}") == v) ? "OK\n":"Failed\n"); 
} 

Код проваливается в повышающем/function_template.hpp линии 766:

result_type operator()(BOOST_FUNCTION_PARMS) const 
{ 
    if (this->empty()) 
    boost::throw_exception(bad_function_call()); 

    return get_vtable()->invoker 
      (this->functor BOOST_FUNCTION_COMMA BOOST_FUNCTION_ARGS); 
} 

Этот код является функцией члена повышения :: function, повышение :: фьюжн :: vector0> & , boost :: spirit :: unused_type const &> и проблема в том, что get_vtable возвращает недопустимый указатель.

+0

Не то, чтобы мне это очень понравилось, но мне интересно, почему здесь может быть причина проголосовать? – user3721426

ответ

2

Основная проблема заключается в том, что конструктор копирования для qi::rule ссылается на исходное правило, которое в вашем случае является локальной переменной. Один из способов избежать этой проблемы - использовать функцию , но это требует незначительного изменения типа возврата по вашей специализации ps_rule.

static typename boost::proto::terminal<qi::rule<iter,std::vector<T,Alloc>()>>::type get() 
{ 
    //[...] (same as before) 
    return res.copy(); 
} 

После того, как вы сделаете это, та же проблема возникает с ps_rule<int> несмотря на то, что, казалось, работали в изоляции. Вы могли бы сделать что-то аналогичное, но в этом случае правило не требуется, было бы лучше (даже с точки зрения производительности) просто использовать что-то вроде:

static qi::int_type get() { return qi::int_; } 

Full Sample (Running on WandBox)

#include <boost/spirit/include/qi.hpp> 
#include <string> 
#include <iostream> 

// Support to simplify 
using iter = std::string::const_iterator; 
void print(std::vector<int> const& v) 
{ 
    std::cout << '['; 
    for (auto i: v) std::cout << i << ','; 
    std::cout << "]"; 
} 

namespace qi = boost::spirit::qi; 

// My rule factory - quite useless if you do not specialise 
template<class T> struct ps_rule; 

// An example of using the factory 
template<class T> 
T from_string(std::string const& s) 
{ 
    T result; 
    iter first { std::begin(s) }; 
    auto rule = ps_rule<T>::get(); 
    qi::phrase_parse(first,std::end(s),rule,qi::space,result); 
    return result; 
} 

// Specialising rule for int 
template<> 
struct ps_rule<int> 
{ 
    static qi::int_type get() { return qi::int_; } 
}; 


// ... and for std::vector (where the elements must have rules) 
template<class T,class Alloc> 
struct ps_rule<std::vector<T,Alloc>> 
{ 
    static typename boost::proto::terminal<qi::rule<iter,std::vector<T,Alloc>()>>::type get() 
    { 
     qi::rule<iter,std::vector<T,Alloc>()> res; 
     res.name("Vector"); 
     res = 
       qi::lit('{') 
      >> ps_rule<T>::get() % ',' 
      >> '}'; 
     return res.copy(); 
    } 
}; 

int main() 
{ 
    // This one works like a charm. 
    std::cout << ((from_string<int>("100") == 100) ? "OK\n":"Failed\n"); 

    std::vector<int> v {1,2,3,4,5,6}; 

    std::cout << ((from_string<std::vector<int>>("{1,2,3,4,5,6}") == v) ? "OK\n":"Failed\n"); 

    std::vector<std::vector<int> > vv {{1,2,3},{4,5,6}}; 

    std::cout << ((from_string<std::vector<std::vector<int>>>("{{1,2,3},{4,5,6}}") == vv) ? "OK\n":"Failed\n"); 

} 

PS : Вы можете сэкономить много специализаций, если используете Spirit's own machinery to create parsers automatically в своем основном шаблоне. Here is an example.

+0

Отлично! Большое спасибо за Вашу помощь. Я просмотрел копию, но не получил правильный тип возврата. – user3721426

+0

Я также был осведомлен о авто парсере, но только просмотрел документацию и, таким образом, не понимал, что я могу специализироваться на них сам. Я продолжу этот подход и удалю ps_rule.unless. У меня возникают проблемы с некоторыми частичными специализациями. – user3721426

+0

BTW: Попробовал проголосовать за ваш ответ, но я, по-видимому, не могу из-за «недостаточных очков» здесь, на SO. – user3721426