2019年6月4日

[C++] std::stringstream cannot output integral type

這次遇到的問題簡單來說就是不知道為什麼程式的 log 有部分訊息被吃掉,然後整份 log 就此大亂。細追分析後發現我們的 log 會先用 std::stringstream 把使用者傳進來的所有參數 (任意數量 & 型態) 轉成 std::string,隨後才把他輸出到 log file 去,而在 std::stringstream 試圖要處裡一個數值型態的參數時會丟出 exception,雖然會被他自己吃掉不會往上丟,但也導致要處理後面的參數時運作情形就不如預期。

進一步往下追原因的時候發現這其實是所有繼承 std::ios_base 的類別都會有的狀況,原因的話簡單來說就是 std::ios_base 內部會主動去處理 locale 相關問題,在這過程中的檢查出狀況時就會丟出一個 exception,而這個 exception 雖然馬上會被吃掉,但會拉起內部的 badbit,下一次要 output 時因為 badbit 被拉起來了,所以會吃掉丟進來的參數但不會真的印出去。

Locale 相關問題如果要講明確一點的話可以參考 locale 這個 header 內的說明,簡單來說就是一些字元、符號上的處理在不同地方、不同語言的處理機制上會不同。當然,C/C++ 會有預設的處理機制,而 std::ios_base 在處理字串上基本上就是直接每個字元照塞,然對於其他的型別 (比方說這次遇到問題的數值型別) 就會先去檢查預設的 locale 有沒有對應的處理機制,沒有的話就丟 std::bad_cast 這個 exception 出去 (我也不知道為什麼是 std::bad_cast ...可以參考 github 上 basic_ios.h 上的實作)。

檢查方式基本上等同於下面這段 code:

#include <locale>

typedef char _CharT;
typedef std::char_traits<char> _Traits;
typedef std::num_put<_CharT, std::ostreambuf_iterator<_CharT, _Traits> > __num_put_type;
auto loc = std::locale ("");
loc = std::locale(loc, "", std::locale::ctype);
std::has_facet<__num_put_type>(loc);

檢查的地方就是用 std::has_facet 去確認目前使用的 locale object 是否有設定對應的 Facet,而預設使用的 local object 等價於上面生出 loc 那幾行。

這次出事就是因為 std::has_facet 不知道為什麼變成 false (以前是 true),然後 log 就大亂了 = =

沒有留言:

張貼留言