今天發現 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() 的關係的。
所以會導致迭代器本身並不自增。
要真正理解還是要搞清楚這個步驟是怎麼做的。。。