2016-08-10 67 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不支持語法,我希望在MSVC編譯時使用X3,而且我也有點不舒服,因爲必須提供隊長。 另一種方法是在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); 
} 

此代碼是在升壓成員函數:: function4 ,升壓::融合:: vector0> & ,boost :: spirit :: unused_type const &> 問題是get_vtable返回一個無效指針。

+0

不是我關心那麼多,但我想知道在這裏投票的理由是什麼? – user3721426

回答

2

你的主要問題是qi::rule的複製構造函數需要引用原始規則,在你的情況下是一個局部變量。避免此問題的一種方法是使用qi::rulecopy成員函數,但這需要稍微更改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

順便說一句:試圖提高你的答案,但我顯然無法因爲SO上的「不足點」。 – user3721426