より多くの精神の狂気-パーサータイプ(ルールとint_parser <>)およびメタプログラミング技術


80

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

私は自分の型システム(型システムが型から文字列へと行き来する)を単一のコンポーネント(Lakosによって定義されている)に統合しようとしています。私が使用していますboost::arrayboost::variantと、boost::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つは効率性、2つは懸念事項をコンポーネントに集中化することです。ポインタを使用すると、最初の理由が無効になります。

3: operator =はno-opとして定義されます。バリアントは、lhsが割り当て前にデフォルトで構築されることを保証します。

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


1
オプション1は、次の場合にのみスレッドセーフではありません。parser_int32_t状態があり、参照が取得されている場合。がステートレスであるか、コピーが作成されている場合、それは安全です。セマンティクスから、コピーが作成されたと言えます。
Matthieu M.

これは非常に紛らわしい懸念事項です。パーサーオブジェクトに状態がないかどうかはわかりません。また、ルールの仕組みには参照と具体的なセマンティクスがあります。つまり、ルールは他のルールへの参照を保持できますが、それら自体も具体的なパーサーになる可能性があり(私は思う)、これらのセマンティクスが具体的なパーサーにどのように適用されるかわかりません。
Hassan Syed

@MatthieuM:そうです、.alias()使用しない限りコピーが作成されます。
ildjarn

@ildjarnですが、ルールは具体的なパーサーではありません:Dルールの内容は式であり、解析ツリーに相当します。
Hassan Syed

1
#1がスレッドセーフであるかどうかを評価することはできませんが、忘れがちなアドバイスを1オンス与えることはできます。静的割り当ては、コンパイラーによって一度だけ評価されます。コードを少しチェックすることを想像してみてください(if(!evaluated_yet)evaluate()else noop())。rbl_int4_parser_rule_definitionの関連するメンバーオブジェクトがどこかで初めて呼び出されると、そのオブジェクトが1回作成されます。これは、グローバルシングルトンを使用するのとほぼ完全に同等です。そのタイプのグローバルシングルトンを使用して同じ問題を解決できますか?(内部順序などを無視します)その場合、これはスレッドセーフである必要があります。
std''OrgnlDave 2012年

回答:


11

質問の全容がわかるかどうかはわかりませんが、ここにいくつかのヒントがあります

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

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

  • ルール alias()

    http://boost-spirit.com/home/articles/doc-addendum/faq/#aliasesで説明されているように

    プロト式を実際に値コピーすることなく、ルールの「論理コピー」を作成できます。FAQにあるように、これは主に遅延結合を可能にするためです

  • Nabialekトリックは何が必要正確かもしれないが、基本的にはそれが遅延し、その後の解析のためのパーサを選択します

    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;
    
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.