もっとスピリットマッドネス - パーサータイプ(ルール vs int_parser<>)とメタプログラミングテクニック 質問する

もっとスピリットマッドネス - パーサータイプ(ルール vs int_parser<>)とメタプログラミングテクニック 質問する

質問は下部に太字で表示され、問題は最後のほうの蒸留コード フラグメントによっても要約されています。

私は型システム (型システムは型から文字列への変換と型から文字列への変換を行います) を単一のコンポーネント (Lakos の定義による) に統合しようとしています。これを実現するためにboost::array、、、を使用しています。型のパーサーとジェネレーターのルールをバリアントに統合したいboost::variantboost::mpl考えています。未定義の型、int4 (下記参照) 型、および int8 型があります。バリアントは と読み取られますvariant<undefined, int4,int8>

int4 特性:

    struct rbl_int4_parser_rule_definition
    {
      typedef boost::spirit::qi::rule<std::string::iterator, rbl_int4()> rule_type;
      
      boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;
      
      rule_type rule;
      
      rbl_int4_parser_rule_definition()
      {
        rule.name("rbl int4 rule");
        rule = parser_int32_t;  
      }
    };
    
    template<>
    struct rbl_type_parser_rule<rbl_int4>
    {
      typedef rbl_int4_parser_rule_definition string_parser;
    };

上記のバリアントは未定義として始まり、その後ルールを初期化します。 問題が発生し、50 ページにわたるエラーが発生し、最終的にそれを追跡することができました。バリアントはoperator=割り当て中に使用され、boost::spirit::qi::int_parser<>別のバリアントに割り当てることはできません (operator=)。

対照的に、未定義の型では問題はありません。

    struct rbl_undefined_parser_rule_definition
    {
      typedef boost::spirit::qi::rule<std::string::iterator, void()> rule_type;
      rule_type rule;
      
      rbl_undefined_parser_rule_definition()
      {
        rule.name("undefined parse rule");
        rule = boost::spirit::qi::eps;
      }
    };
    
    template<>
    struct rbl_type_parser_rule<rbl_undefined>
    {
      typedef rbl_undefined_parser_rule_definition string_parser;
    };

問題の要約:

    #include <string>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/variant.hpp>
    #include <boost/cstdint.hpp>
    
    typedef boost::spirit::qi::rule<std::string::iterator,void()> r1;
    typedef boost::spirit::qi::rule<std::string::iterator,int()> r2;
    
    typedef boost::variant<r1,r2> v;
    
    int main()
    {
      /*
      problematic
      boost::spirit::qi::int_parser<int32_t> t2;
      boost::spirit::qi::int_parser<int32_t> t1;
      
      
      t1 = t2;
      */
    
      //unproblematic
      r1 r1_;
      r2 r2_;
      r1_ = r2_;
    
      v v_;
      // THIS is what I need to do.
      v_ = r2();
    }

具体的なパーサとルールの間には意味的なギャップがあります。私の頭は今煙を上げているので、実用主義については考えません。私の質問は、この問題をどうやって解決するかということです。この問題を解決するには 3 つのアプローチが考えられます。

1つ:静的関数メンバー:

    struct rbl_int4_parser_rule_definition
    {
      typedef boost::spirit::qi::rule<std::string::iterator, rbl_int4()> rule_type;
      
      //boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;
      
      rule_type rule;
      
      rbl_int4_parser_rule_definition()
      {
        static boost::spirit::qi::int_parser<rbl_int4> parser_int32_t;
    
        rule.name("rbl int4 rule");
        rule = parser_int32_t;  
      }
    };

アプローチ 1 ではスレッドセーフなコードが妨げられると思います。

二:積分パーサーは shared_ptr にラップされています。私が型付けシステムに TMP を使っている理由は 2 つあります。1 つは効率性、もう 1 つはコンポーネントへの関心の集中化です。ポインタを使用すると、最初の理由が達成されません。

三つ:operator= は何も実行しないものとして定義されます。バリアントは、lhs代入前に がデフォルトで構築されることを保証します。

編集:オプション 3 が最も理にかなっていると思います (operator= は何も行いません)。ルール コンテナーが作成されると、それは変更されず、型のルール特性をそのオフセットに強制的に割り当てているだけです。

ベストアンサー1

質問の全容を理解しているかどうかはわかりませんが、いくつかヒントを挙げておきます。

  • コメントされた行は、// THIS is what I need to do.私の場合は問題なくコンパイルされます (問題は解決しましたか? 実際にはルールではなくパーサーを割り当てることを意味していたのでしょうか?)

  • 関数ローカルの初期化は、static最新の標準 (C++11) でスレッドセーフであると定義されています。コンパイラが C++0x スレッドをサポートしているかどうかを確認してください。(ちなみに、初期化子がスローした場合、初期化ステートメントを渡すと、再度初期化が試行されます)。

  • ルールalias()

    記載の通りhttp://boost-spirit.com/home/articles/doc-addendum/faq/#aliases

    実際にプロト式を値コピーしなくても、ルールの「論理コピー」を作成できます。FAQにあるように、これは主に遅延バインディングを可能にするためです。

  • ナビアレクトリックまさにあなたが必要としているものかもしれません。基本的には、後続の解析のためにパーサーを遅延選択するものです。

    one = id;
    two = id >> ',' >> id;
    
    keyword.add
        ("one", &one)
        ("two", &two)
        ;
    
    start = *(keyword[_a = _1] >> lazy(*_a));
    

    keywordあなたの文脈では、次のように定義できます

    qi::symbols<char, qi::rule<Iterator>*> keyword;
    

    セマンティックアクションの属性を使ってすべての作業を行う。あるいは、

    qi::symbols<char, qi::rule<Iterator, std::variant<std::string,int>() >*> keyword;
    
  • ルールを同じタイプにまとめます(基本的には前の行のように)

    ここが私が混乱している部分です。型システムを統一したいとおっしゃっていますが、厳密に型指定されたパーサー (個別の属性シグネチャ) は必要ないかもしれません。

    typedef boost::variant<std::string,int> unified_type;
    typedef qi::rule<std::string::iterator, unified_type() > unified_rule;
    
    unified_rule rstring = +(qi::char_ - '.');
    unified_rule rint    = qi::int_;
    
    unified_rule combine = rstring | rint;
    

おすすめ記事