5

iterator_rangeからトークンの値を変換しようとすると、次のトークンを読み取ろうとしたときにレクサーが失敗します。

トークン定義を保持するTokens構造体は次のとおりです:(これは適切ではないと思いますが、念のために含めています)。

template <typename Lexer>
struct Tokens : boost::spirit::lex::lexer<Lexer>
{
    Tokens();

    boost::spirit::lex::token_def<std::string> identifier;
    boost::spirit::lex::token_def<std::string> string;
    boost::spirit::lex::token_def<bool> boolean;
    boost::spirit::lex::token_def<double> real;
    boost::spirit::lex::token_def<> comment;
    boost::spirit::lex::token_def<> whitespace;
};

template <typename Lexer>
Tokens<Lexer>::Tokens()
{
    // Define regex macros
    this->self.add_pattern
        ("LETTER", "[a-zA-Z_]")
        ("DIGIT", "[0-9]")
        ("INTEGER", "-?{DIGIT}+")
        ("FLOAT", "-?{DIGIT}*\\.{DIGIT}+");

    // Define the tokens' regular expressions
    identifier = "{LETTER}({LETTER}|{DIGIT})*";
    string = "\"[a-zA-Z_0-9]*\"";
    boolean = "true|false";
    real = "{INTEGER}|{FLOAT}";
    comment = "#[^\n\r\f\v]*$";
    whitespace = "\x20\n\r\f\v\t+";

    // Define tokens
    this->self
        = identifier
        | string
        | boolean
        | real
        | '{'
        | '}'
        | '<'
        | '>';

    // Define tokens to be ignored
    this->self("WS")
        = whitespace
        | comment;
}

トークンとレクサーのタイプの定義は次のとおりです。

typedef lex::lexertl::token<char const*> TokenType;
typedef lex::lexertl::actor_lexer<TokenType> LexerType;

トークンを読み取り、その値を文字列に変換するために使用しているコードは次のとおりです。

Tokens<LexerType> tokens;

std::string string = "9index";
char const* first = string.c_str();
char const* last = &first[string.size()];
LexerType::iterator_type token = tokens.begin(first, last);
LexerType::iterator_type end = tokens.end();

//typedef boost::iterator_range<char const*> iterator_range;
//const iterator_range& range = boost::get<iterator_range>(token->value());
//std::cout << std::string(range.begin(), range.end()) << std::endl;

++token;

token_is_valid(*token); // Returns false ONLY if I uncomment the above code

このコードの出力は「9」です(最初の数値を読み取り、ストリームに「インデックス」を残します)。この時点でstring(first、last)の値を出力すると、「ndex」と表示されます。何らかの理由で、レクサーはその「i」文字で失敗していますか?

std :: stringstreamを使用して変換を試みましたが、これにより次のトークンも無効になります。

std::stringstream out;
out << token->value();
std::cout << out.str() << std::endl;

++token;

token_is_valid(*token); // still fails

最後に、トークンの値をcoutに送信するだけで、次のトークンは有効になります。

std::cout << token->value() << std::endl;

++token;

token_is_valid(*token); // success, what?

token-> value()によって返されるiterator_rangeがどのように機能するかについて私は何が欠けていますか?文字列に変換するために使用した方法はどちらも、integer_rangeまたはレクサーの文字の入力ストリームを変更するようには見えません。

編集:コメントの返信が短すぎて何が起こったのかを完全に説明できないため、ここにこれを追加します。

私はそれを考え出した。seheとdrhirschが指摘したように、私の最初の質問のコードは、私が実際に行っていることの滅菌バージョンでした。テストフィクスチャクラスでgtestユニットテストを使用してレクサーをテストしています。そのクラスのメンバーとして、指定された文字列から最初と最後のイテレータ(フィクスチャのデータメンバー)を割り当てるvoid scan(const std :: string&str)があります。問題は、この関数を終了するとすぐに、const std :: string&strパラメーターがスタックからポップされて存在しなくなり、フィクスチャーのデータメンバーであってもこれらのイテレーターが無効になることです。

の教訓:イテレータがlexer :: begin()が参照するオブジェクトは、トークンを読み取ることを期待している限り存在する必要があります。

私はインターネットで私のばかげた間違いを文書化するよりもこの質問を削除したいのですが、コミュニティを助けるために私はそれを残すべきだと思います。

4

1 に答える 1

5

与えられたコードから判断すると、コンパイラ/ライブラリのバグを見ているようです。次の組み合わせのいずれかで問題を再現できません。

Edit Now には、clang++ と boost 1_49_0 が含まれています。Valgrind は、選択された数のテストされたケースで問題なく動作します。

  • clang++ 2.9、-O3、ブースト 1_46_1
  • clang++ 2.9、-O0、ブースト 1_46_1
  • clang++ 2.9、-O3、ブースト 1_48_0
  • clang++ 2.9、-O0、ブースト 1_48_0
  • clang++ 2.9、-O3、ブースト 1_49_0
  • clang++ 2.9、-O0、ブースト 1_49_0

  • gcc 4.4.5、-O0、ブースト 1_42_1

  • gcc 4.4.5、-O0、ブースト 1_46_1
  • gcc 4.4.5、-O0、ブースト 1_48_0
  • gcc 4.4.5、-O0、ブースト 1_49_0
  • gcc 4.4.5、-O3、ブースト 1_42_1
  • gcc 4.4.5、-O3、ブースト 1_46_1
  • gcc 4.4.5、-O3、ブースト 1_48_0
  • gcc 4.4.5、-O3、ブースト 1_49_0
  • gcc 4.6.1、-O0、ブースト 1_46_1
  • gcc 4.6.1、-O0、ブースト 1_48_0
  • gcc 4.6.1、-O0、ブースト 1_49_0
  • gcc 4.6.1、-O3、ブースト 1_42_1
  • gcc 4.6.1、-O3、ブースト 1_46_1
  • gcc 4.6.1、-O3、ブースト 1_48_0
  • gcc 4.6.1、-O3、ブースト 1_49_0

テストされた完全なコード:

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

namespace qi    = boost::spirit::qi;
namespace lex   = boost::spirit::lex;

template <typename Lexer>
struct Tokens : lex::lexer<Lexer>
{
    Tokens();

    lex::token_def<std::string> identifier;
    lex::token_def<std::string> string;
    lex::token_def<bool> boolean;
    lex::token_def<double> real;
    lex::token_def<> comment;
    lex::token_def<> whitespace;
};

template <typename Lexer>
Tokens<Lexer>::Tokens()
{
    // Define regex macros
    this->self.add_pattern
        ("LETTER", "[a-zA-Z_]")
        ("DIGIT", "[0-9]")
        ("INTEGER", "-?{DIGIT}+")
        ("FLOAT", "-?{DIGIT}*\\.{DIGIT}+");

    // Define the tokens' regular expressions
    identifier = "{LETTER}({LETTER}|{DIGIT})*";
    string = "\"[a-zA-Z_0-9]*\"";
    boolean = "true|false";
    real = "{INTEGER}|{FLOAT}";
    comment = "#[^\n\r\f\v]*$";
    whitespace = "\x20\n\r\f\v\t+";

    // Define tokens
    this->self
        = identifier
        | string
        | boolean
        | real
        | '{'
        | '}'
        | '<'
        | '>';

    // Define tokens to be ignored
    this->self("WS")
        = whitespace
        | comment;
}

////////////////////////////////////////////////
typedef lex::lexertl::token<char const*> TokenType;
typedef lex::lexertl::actor_lexer<TokenType> LexerType;

int main(int argc, const char *argv[])
{
    Tokens<LexerType> tokens;

    std::string string = "9index";
    char const* first = string.c_str();
    char const* last = &first[string.size()];
    LexerType::iterator_type token = tokens.begin(first, last);
    LexerType::iterator_type end = tokens.end();

    typedef boost::iterator_range<char const*> iterator_range;
    const iterator_range& range = boost::get<iterator_range>(token->value());
    std::cout << std::string(range.begin(), range.end()) << std::endl;

    ++token;

    // Returns false ONLY if I uncomment the above code
    std::cout << "Next valid: " << std::boolalpha << token_is_valid(*token) << '\n'; 

    return 0;
}
于 2012-05-02T09:10:48.267 に答える