這篇某種意義上算是之前《攔截 std::cout 的輸出結果》的延伸。
在把 std::cout
等內容攔截下來後,自然就是要想辦法拿來用了。但是要怎麼知道裡面有新東西、該去讀資料呢?
由於 std::streambuf
(參考)的設計應該是沒有辦法掛 callback 之類的東西,所以如果想要有類似的功能,應該就是得自己去擴展一個有 callback 功能的 stream buffer 了。
下面就是一個簡單的例子,會每行執行一次 callback:
class funcStrBuf : public std::streambuf { public: funcStrBuf(std::function<void(std::string_view)> f) : std::streambuf() { funcCallback = f; } protected: int_type overflow(int_type ch) override { if (ch == '\n') runCallback(); else sContent += ch; return std::streambuf::traits_type::not_eof(ch); } int sync() override { runCallback(); return 0; } void runCallback() { if (sContent.size() > 0) { funcCallback(sContent); sContent.clear(); } } protected: std::string sContent; std::function<void(std::string_view)> funcCallback = [](std::string_view) {}; };
這邊的 funcStrBuf
這個類別內部是使用 sContent
這個字串來做為內部的記憶體管理;而當有東西輸出給他的時候,就會每個字元一個一個透過 overflow()
這個函式(參考)送進來,所以這邊必須要重新實做這個函式來做自己的處理。
這邊由於是打算每行執行一次,所以這邊的 overflow()
就是去檢查拿到的字元是否是換行字元的「\n
」(\r
這邊先不管),如果是的話就去呼叫 runCallback()
這個函式;否則就把字元附加到 sContent
後面。
而在 runCallback()
中,則是先檢查 sContent
是否是空字串,如果不是的話就去呼叫 callback function 的物件 funcCallback
、然後再把 sContent
清空。
另外,這邊也重新實做了 sync()
這個函式(參考),主要是用來對應 flush 這類的動作;這個其實不見得需要,基本上是看個人的需求了。
像如果還是要用來攔截 std::cout
的結果來使用的話,大致上會是像下面的形式:
// get standard buffer auto* pStdBuf = std::cout.rdbuf(); // use another streambuf std::vector<std::string> vData; funcStrBuf buf([&vData](std::string_view sv) { vData.emplace_back(sv); }); //functionbuf buf; std::cout.rdbuf(&buf); // output std::cout << "xxx" << std::flush; std::cout << "Hello world" << std::endl; std::cout << 1 << ":" << 2.5 << "\n" << 'a' << "as" << std::endl; // restore buffer std::cout.rdbuf(pStdBuf); // output for(const auto& s : vData) std::cout << "[" << s << "]\n";
這邊基本上就是和之前一樣、透過 rdbuf()
這個函式來重新設定 std::cout
的輸出 buffer;而這邊則是使用前面定義的 funcStrBuf
,所設定的 callback 基本上就是把拿到的字串丟到 vData
這個陣列裡面儲存下來,最後在一起輸出。
這樣的程式在執行後,結果會像下面這樣:
[xxx] [Hello world] [1:2.5] [aas]
而如果有需求的話,也可以在 callback 被執行的時候,加上時間資訊之類的,這樣拿來分析輸出的資訊就更有用了。
如果要搭配圖形介面顯示的話,也可以把拿到的文字丟到圖形介面裡面來顯示;實際上 Heresy 這邊也有拿來搭配 Qt 使用,應該是沒問題的。
附註:由於 std::stringbuf
雖然有提供內部資料的管理,但是在呼叫內部的函式時會做一些額外的處理,所以不建議繼承 std::stringbuf
來開發。