2016-08-10 15 views
0

残念ながら(MSVCがサポートされている必要があるため)qiを使用して一般的なパーサ要素を作成しようとしていますが、X3を使用することはできません。 アイデアは、テンプレート構造体を持っていることです。qiを使用して汎用パーザを作成するにはどうすればよいですか?

私はこのように使用することができ
template<class T> struct parse_type; 

:主な目的は、簡単のためにできるようにすることです。この

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

よう

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

このコードは、ブーストのメンバ関数です::機能4 、後押し::融合を:: boost :: spirit :: unused_type const &> となり、問題はget_vtableが無効なポインタを返すことです。

+0

私はそれほど気にしませんが、ここでの下降理由はどうなっているのでしょうか? – user3721426

答えて

2

あなたの主な問題は、qi::ruleのコピーコンストラクタが元のルール(あなたの場合はローカル変数)を参照することです。この問題を回避できる方法の1つは、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を削除するでしょう。そうでなければ、私はいくつかの部分的な専門化に問題があります。 – user3721426

+0

BTW:あなたの回答をアップしようとしましたが、ここでは「不十分な点」のために私は明らかにできません。 – user3721426

関連する問題