3

コードの何が問題なのかわかりません。Boost のテンプレートは私を夢中にさせます! 私はこれらすべてについて頭も尻尾も作ることができないので、ただ尋ねなければなりませんでした。

これの何が問題なのですか?

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

void parsePathTest(const std::string &path)
{
    namespace lambda = boost::lambda;
    using namespace boost::spirit;

    const std::string permitted = "._\\-#@a-zA-Z0-9";
    const std::string physicalPermitted = permitted + "/\\\\";
    const std::string archivedPermitted = permitted + ":{}";

    std::string physical,archived;

    // avoids non-const reference to rvalue
    std::string::const_iterator begin = path.begin(),end = path.end();

    // splits a string like "some/nice-path/while_checking:permitted#symbols.bin"
    // as physical = "some/nice-path/while_checking"
    // and archived = "permitted#symbols.bin" (if this portion exists)
    // I could barely find out the type for this expression
    auto expr
        =   ( +char_(physicalPermitted) ) [lambda::var(physical) = lambda::_1]
            >> -(
                    ':'
                    >> (
                           +char_(archivedPermitted) [lambda::var(archived) = lambda::_1]
                       )
                )
        ;

    // the error occurs in a template instantiated from here
    qi::parse(begin,end,expr);

    std::cout << physical << '\n' << archived << '\n';
}

エラーの数は膨大です。これを自分でコンパイルするのを手伝いたい人をお勧めします(私を信じてください、ここに貼り付けるのは実用的ではありません)。最新の TDM-GCC バージョン (GCC 4.4.1) と Boost バージョン 1.39.00 を使用しています。

static_assertおまけとして、C++0x の新しい機能がこの意味で Boost に役立つかどうか、上記で引用した実装が良いアイデアかどうか、または Boost の String Algorithms ライブラリを使用する必要があるかどうか、さらに 2 つのことをお聞きしたいと思います。 . 後者はおそらくはるかに優れたパフォーマンスを提供しますか?

ありがとう。

- 編集

次の非常に最小限のサンプルは、最初は上記のコードとまったく同じエラーで失敗します。

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

int main()
{
    using namespace boost::spirit;

    std::string str = "sample";
    std::string::const_iterator begin(str.begin()), end(str.end());

    auto expr
        =   ( +char_("a-zA-Z") )
        ;

    // the error occurs in a template instantiated from here
    if (qi::parse(begin,end,expr))
    {
        std::cout << "[+] Parsed!\n";
    }
    else
    {
        std::cout << "[-] Parsing failed.\n";
    }

    return 0;
}

-- 編集 2

私の古いバージョンの Boost (1.39) でなぜ動作しなかったのかはまだわかりませんが、Boost 1.42 にアップグレードすると問題が解決しました。次のコードは、Boost 1.42 で完全にコンパイルおよび実行されます。

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

int main()
{
    using namespace boost::spirit;

    std::string str = "sample";
    std::string::const_iterator begin(str.begin()), end(str.end());

    auto expr
        =   ( +qi::char_("a-zA-Z") ) // notice this line; char_ is not part of 
                                     // boost::spirit anymore (or maybe I didn't 
                                     // include the right headers, but, regardless, 
                                     // khaiser said I should use qi::char_, so here 
                                     // it goes)
        ;

    // the error occurs in a template instantiated from here
    if (qi::parse(begin,end,expr))
    {
        std::cout << "[+] Parsed!\n";
    }
    else
    {
        std::cout << "[-] Parsing failed.\n";
    }

    return 0;
}

ヒントをありがとう、hkaiser。

4

1 に答える 1

7

いくつかの注意事項: a) Boost V1.39 および V1.40 で配布されている Spirit V2 ベータ版は使用しないでください。代わりに少なくとも Spirit V2.1 (Boost V1.41 でリリース) を使用してください。多くのバグ修正とパフォーマンス強化 (コンパイル時間とランタイム パフォーマンスの両方) が含まれているためです。Boost のバージョンを切り替えることができない場合は、こちらの手順をお読みください。b) Spirit V2.x で boost::lambda または boost::bind を使用しないようにしてください。はい、私は知っています、ドキュメントはそれが機能すると言っていますが、あなたは自分が何をしているのかを知る必要があります. 代わりに boost::phoenix 式を使用してください。Spirit は Phoenix について「知っている」ので、セマンティックアクションの記述が容易になります。Phoenix を使用する場合、コードは次のようになります。

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

namespace qi = boost::spirit::qi;
namespace phoenix = boost::phoenix;

std::string physical, archived;  
auto expr 
    =   ( +char_(physicalPermitted) ) [phoenix::ref(physical) = qi::_1] 
    >> -( 
            ':' 
            >> ( +char_(archivedPermitted) )[phoenix::ref(archived) = qi::_1] 
        ) 
    ; 

しかし、Spirit の組み込みの属性伝播ルールを利用すると、パーサー全体がさらに単純になります。

std::string physical;
boost::optional<std::string> archived;  

qi::parse(begin, end, 
    +qi::char_(physicalPermitted) >> -(':' >> +qi::char_(archivedPermitted)),
    physical, archived);

つまり、セマンティック アクションはまったく必要ありません。属性の処理についてさらに詳しい情報が必要な場合は、Spirit の Web サイトで属性の魔法に関する一連の記事を参照してください。

編集:

static_assert の質問について: yes static_assert を使用すると、コンパイラ エラーをできるだけ早くトリガーするために使用できるため、エラー メッセージを改善できます。実際、Spirit はすでにこの手法を広く使用しています。しかし、すべての場合にこれらの巨大なエラー メッセージが表示されないようにユーザーを保護することはできません。一般に、エラー メッセージのサイズを小さくするために使用できたのは、概念 (残念ながら新しい C++ 標準にはなりませんでした) だけでした。

Boost の String Algorithms に関する質問: 確かに、このライブラリを単純なタスクに利用することは可能です。Boost.Tokenizer を使用したほうがよい場合もあります (入力文字列を ':' で分割するだけでよい場合)。Spirit のパフォーマンスは、対応する文字列アルゴリズムのパフォーマンスに匹敵するはずですが、これは確かに、作成するコードに依存します。使用されている文字列アルゴリズムが入力文字列データに対して 1 回のパスを必要とすると仮定すると、Spirit は高速にはなりません (1 回のパスも実行するため)。

Boost String アルゴリズムも Boost Tokenizer も、一致した文字を検証することはできません。あなたの Spirit 文法は、文字クラスで指定した文字のみに一致します。したがって、この照合/検証が必要な場合は、Spirit または Boost Regex を使用する必要があります。

于 2010-02-10T01:11:34.903 に答える