[英]How can I inject a newline when a std::ostream object is std::flush'ed?
我如何最低限度地包裝std::ofstream
以便對sync
( std::flush
) 的任何調用都變成對std::endl
的調用。
(以下所有內容都是對“你為什么要那樣做?”問題的回答,與上述問題無關)
我有一個包含自定義std::ostream
實現(環繞 zmq)的應用程序,我們稱它為zmq_stream
。
這個zmq_stream
也在內部實現了一個自定義的streambuffer
,並且工作得很好。
這種設計的動機之一是在我的單元測試中,我可以簡單地將單元測試中的zmq_stream
替換為常規的舊std::ostringstream
並測試結果,完全隱藏 .network 層。
應用程序本身的功能之一是通過命令行標志將應用程序 output 重定向到文件或標准輸出。
再一次,在啟動時使用基本開關,我可以將zmq_stream
、 std::ofstream
或普通的舊std::cout
傳遞到我的主循環。
這是完成這樣的事情:
std::ostream* out;
switch(cond)
{
case 1:
out = &std::cout;
break;
case 2:
out = new ofstream("file");
break;
case 3:
out = new zmq_stream(method, endpoints);
break;
}
main_loop(out);
zmq_stream
的用法如下:
zmq_stream out(method, endpoints);
out << "do something " << 42 << " or other";
out << "and another" << std::flush; // <- full constructed buffer is sent over the network
注意:根據設計,我在刷新到 .network 時使用std::flush
而不是std::endl
。 我不希望 append 換行到所有 my.network 傳輸。 因此,應用程序的 all.network 輸出使用std::flush
而不是std::endl
並且更改它不是正確的答案。
問題:出於測試目的,一切都按預期工作,對於std::cout
和std::ofstream
選項,我希望能夠在調用std::flush
時插入換行符,以便 my.network 傳輸可以在stdout
上分隔成換行符。 這將允許我將它們 pipe 添加到標准的 posix 工具套件中......
沒有這種注入,就沒有辦法確定(除了計時)網絡邊界在哪里。
我在這里尋找盡可能少的覆蓋,這樣我就不必寫出一堆新類。 理想情況下,重寫sync()
虛方法以便它調用overflow('\n');
然后調用 base sync()
。
然而,事實證明這比我預期的更具挑戰性,所以我想知道我是否做錯了。
我如何包裝文件 stream.
您可以編寫一個<<
重載來攔截std::flush
並添加\n
。 我只是概述了這個想法,沒有生產就緒代碼。
#include <iostream>
struct foo {
std::ostream& out = std::cout;
template <class T>
foo& operator<<(const T& t) {
out << t;
return *this;
}
foo& operator<<(std::ostream& (*f)(std::ostream&)) {
if (f == &std::flush<std::ostream::char_type,std::ostream::traits_type>) { out << "\n"; }
out << f;
return *this;
}
};
int main() {
foo f;
f << "a" << std::endl << "b" << std::flush << "c";
}
在 output 中,您可以看到現在std::endl
和std::flush
都添加了一個\n
。 std::endl
因為它無論如何都這樣做了,而std::flush
因為包裝器添加了它。 現場演示:
a
b
c
通常參考std
function 應該小心。 僅當它們是顯式可尋址函數時才允許。 實際上我沒有發現提到過,但是將它們作為 function 指針傳遞是 io-manipulators 應該如何使用。
我寫的初始解決方案不起作用,因為它是一個編譯時解決方案,如果不重新實現 inheritance 就無法打開它(這樣main_loop
function 得到一個基數 class 作為參數)。
相反,這是原始問題的答案:
#include <iostream>
#include <fstream>
#include <iomanip>
typedef std::ostringstream zmq_stream;
template <class T>
class newline_injector_streambuf: public std::basic_streambuf<T> {
public:
using int_type = typename std::basic_streambuf<T>::int_type;
newline_injector_streambuf(std::basic_streambuf<T>& dest) : sink(dest) {}
protected:
virtual int_type sync() override { overflow('\n'); return sink.pubsync(); }
virtual int_type overflow(int_type c) override { return sink.sputc(c); }
std::basic_streambuf<T>& sink;
};
template <class T>
struct newline_injector_stream : public std::basic_ostream<typename T::char_type> {
newline_injector_streambuf<typename T::char_type> buf;
newline_injector_stream(T* file) : buf(*file->rdbuf())
{
std::basic_ostream<typename T::char_type>::rdbuf(&buf);
}
};
void test(std::ostream& out)
{
out << std::setfill('x') << std::setw(10) << "" << std::flush;
out << "Hello, world!" << std::flush << "asdf" << std::endl;
}
int main() {
newline_injector_stream out1(&std::cout);
newline_injector_stream out2(new std::ofstream("test.output", std::ios::out | std::ios::ate));
test(std::cout);
test(out1);
test(out2);
return 0;
}
這是由於 @463035818-is-not-a-number 的初始答案而設計的解決方案:
struct zmq_stream;
template<typename T>
struct output_device {
T& output;
output_device(T& backing_stream) : output(backing_stream){}
template <typename U>
output_device& operator<<(const U& u)
{
output << u;
return *this;
}
output_device& operator<<(std::ostream& (*f)(std::ostream&)) {
if constexpr (!std::is_same<T, zmq_stream>::value)
if (f == &std::flush<std::ostream::char_type,std::ostream::traits_type>)
output << "\n";
output << f;
return *this;
}
};
// Usage:
int main()
{
/* prelude code */
auto out1 = output_device(std::cout);
auto out2 = output_device(std::ofstream("filename"));
auto out3 = output_device(zmq_stream(method, endpoints));
out1 << "foo" << std::flush << "bar"; // results in foo\nbar
out2 << "foo" << std::flush << "bar"; // results in foo\nbar
out3 << "foo" << std::flush << "bar"; // results in foobar
}
template <class T>
class separator: public std::basic_streambuf<T> {
public:
using int_type = typename std::basic_streambuf<T>::int_type;
using char_type = typename std::basic_streambuf<T>::char_type;
using traits_type = typename std::basic_streambuf<T>::traits_type;
separator(std::basic_streambuf<T> *dest) : sink(dest) {}
protected:
virtual int_type sync() override {
if (nullptr == sink)
return 1;
//std::basic_streambuf<T>::overflow('\n');
overflow('\n');
return sink->pubsync();
}
virtual int_type overflow(int_type c) override {
if (sink == nullptr)
return 1;
return sink->sputc(c);
}
std::basic_streambuf<T> *sink = nullptr;
};
template <class T> struct newline_injector_stream : public std::basic_ostream<T> {
separator<T> buf;
public:
newline_injector_stream(std::basic_ostream<T> &file) : buf(file.rdbuf())
{
std::basic_ostream<T>::rdbuf(&buf);
}
};
main_loop
function 簽名現在從采用std::ostream*
類型變為output_device&
類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.