boost tokenizer 坑

今天發現 boost tokenizer容易用錯的地方,記錄一下。


  1 #include <unordered_map>
  2 #include <iostream>       
  3 #include <map>
  4 #include <string>         
  5 #include <vector>         
  6 #include <boost/tokenizer.hpp>
  7 
  8 
  9 using namespace std;
 10 using namespace boost;
 11 
 12 int main() {
 13 
 30 //    vector<string> str_vec;
 31 //    str_vec.push_back("hello");
 32 //    str_vec.push_back("world");
 33 //    auto itr = str_vec.begin();
 34 //    const string& str = *itr;
 35 //    cout << str << endl;
 36 //    ++itr;
 37 //    cout << str << endl;
 38 
 39     string src("hello world");
 41     tokenizer<> outer_tokens(src); 
 42     tokenizer<>::iterator outer_tok_itr = outer_tokens.begin();
 43     const string& str_1 = *outer_tok_itr;
 44     cout << str_1 << endl;
 46     ++outer_tok_itr;
 47     cout << str_1 << endl;
 49 }


輸出如下:

hello
world


就是在用引用的時候

const string& str_1 = *outer_tok_itr;
++outer_tok_itr 的操作導致兩次輸出str_1結果不一樣。
因爲差點嚇哭,所以試了一下vector的迭代器壓壓驚,就是註釋掉的部分,結果兩次輸出的都是hello,事實證明我原來的三觀還算是正的。。
</pre><pre name="code" class="cpp"> 40     string src("hello world");
 41     tokenizer<> outer_tokens(src);
 42     tokenizer<>::iterator outer_tok_itr = outer_tokens.begin();
 43     const string& str_1 = *outer_tok_itr; 
 44     cout << str_1 << endl;
 45 //    ++outer_tok_itr;
 46     tokenizer<>::iterator new_itr = outer_tok_itr;
 47     std::advance(outer_tok_itr, 1); 
 48     cout << str_1 << endl;
 49     
 50     cout << *new_itr << endl;
 51     cout << *outer_tok_itr << endl;

輸出:

hello
world
hello
world

new_itr確實能輸出正確結果。。


先記下這個坑,有時間再查原因:(



更新:

今天又發現另外一個問題,記錄一下。

在線上環境中發現 tokenizer iterator 明顯已經越界,但是還在對這個迭代器解引用,這應該是一個很明顯的bug,但是線上卻是正常的!怎麼會這樣!

我寫了簡單的測試代碼,發現對越界的迭代器解引用是會報錯的。

我確認了幾遍測試代碼和線上代碼高度一致,然後也在同一臺開發機上編譯,也在同一臺機器上運行。都是一樣的,爲啥表現不一致!

後來想到問題出在編譯參數上,我編寫測試程序是用 g++ 直接默認參數編譯的,但是我們工程是用的 blade 構建工具,

排查了一遍參數設置,發現問題在於blade 默認定義了NODEBUG, 從而關閉了 assertion。


恩,找到線上爲啥沒有報錯的原因,但是,對越界的迭代器解引用怎麼樣也不應該輸出正確結果吧。

一般也會 segment fault  吧(試了一下對 vector 的越界 iterator 解引用確實會 segment  fault)。

其實找到問題在哪裏也不用管這種異常情況下程序是怎麼表現的了,未定義行爲怎麼表現都是可以的。

但是還是想儘可能瞭解一下。。。

這就是 boost tokenizer 的特異之處了。。


typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
boost::char_separator<char> outer_sep(" ");
string src("hello world hahah");
Tokenizer outer_tokens(src, outer_sep);
Tokenizer::iterator outer_tok_itr = outer_tokens.begin();
while(outer_tok_itr != outer_tokens.end()) {
  cout << "not end yet" << endl;
  ++outer_tok_itr;
}
cout << *outer_tok_itr << endl;                         // output hahah
cout << *outer_tokens.end() << endl;                    // output ""(nothing)

++outer_tok_itr;
cout << *outer_tok_itr << endl;                         // output hahah

(以上代碼編譯時定義 NODEBUG ,否則是肯定 assert 的)

while 循環出來 itr 就已經和 end() 相等了,但是解引用可是不一樣的。

輸出見註釋,另,即使自增 itr 之後,輸出的仍然是 hahah。

其實這個還算好理解,看了下源代碼,在自增執行TokenizerFunc時(此處爲char_separator)時,是會檢查當前的迭代器是 end() 的關係的。

所以會導致迭代器本身並不自增。


要真正理解還是要搞清楚這個步驟是怎麼做的。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章