2014-12-06 102 views
0

我想要在cout上重定向的每一行的開頭都有一個前綴的ostream; 我試試這個:帶前綴的C++ cout

#include <iostream> 
#include <thread> 
class parallel_cout : public std::ostream 
{ 
public: 
parallel_cout(std::ostream& o):out(o){} 

template <typename T> 
std::ostream& operator<< (const T& val) 
{ 
    out << "prefix " << val; 
    return *this; 
} 

std::ostream& out; 

}; 

int main() 
{ 
parallel_cout pc(std::cout); 
pc<<"a\nb"<<"c\n"; 
} 

,但我在輸出

prefix a 
b 

無C。爲什麼這個?

+0

通常,從std :: stream派生它不是一個好主意 - 它沒有虛擬析構函數,並且代碼顯示了輸出操作在派生類中的效果如何,因爲您正在返回對基類的引用。我會嘗試擴展到一個完整的答案,除非有人打我:) – 2014-12-06 20:52:08

+2

您的模板應該返回'parallel_cout&',而不是通用'ostream&'。 – Deduplicator 2014-12-06 20:52:15

+2

@martin_pr'std :: ostream'有一個虛擬構造函數(繼承自'std :: ios_base')。此外,'std :: fstream'和'std :: stringstream'也是派生類,但它們工作正常。你想從IOStreams基類派生的主要原因是環繞自定義的'std :: streambuf',這是具體的流類。 – 0x499602D2 2014-12-06 20:54:29

回答

7

的方式修改的std::ostream行爲是通過重載任何輸出運算符!相反,您從std::streambuf派生類並取代virtual函數overflow()sync()。在你的情況下,你可能會創建一個過濾流緩衝區,也就是說,你可以將另一個std::streambuf作爲參數,並向該流緩衝區中寫入一個修改過的字符流。

下面是一個簡單的例子:

#include <iostream> 

class prefixbuf 
    : public std::streambuf 
{ 
    std::string  prefix; 
    std::streambuf* sbuf; 
    bool   need_prefix; 

    int sync() { 
     return this->sbuf->pubsync(); 
    } 
    int overflow(int c) { 
     if (c != std::char_traits<char>::eof()) { 
      if (this->need_prefix 
       && !this->prefix.empty() 
       && this->prefix.size() != this->sbuf->sputn(&this->prefix[0], this->prefix.size())) { 
       return std::char_traits<char>::eof(); 
      } 
      this->need_prefix = c == '\n'; 
     } 
     return this->sbuf->sputc(c); 
    } 
public: 
    prefixbuf(std::string const& prefix, std::streambuf* sbuf) 
     : prefix(prefix) 
     , sbuf(sbuf) 
     , need_prefix(true) { 
    } 
}; 

class oprefixstream 
    : private virtual prefixbuf 
    , public std::ostream 
{ 
public: 
    oprefixstream(std::string const& prefix, std::ostream& out) 
     : prefixbuf(prefix, out.rdbuf()) 
     , std::ios(static_cast<std::streambuf*>(this)) 
     , std::ostream(static_cast<std::streambuf*>(this)) { 
    } 
}; 

int main() 
{ 
    oprefixstream out("prefix: ", std::cout); 
    out << "hello\n" 
     << "world\n"; 
} 

流緩衝概念性地保持一個內部緩衝器,該緩衝器是,然而,沒有設置在上述的例子。每次沒有將字符寫入輸出緩衝區的空間時,virtual函數overflow()被調用一個字符(也可以使用通常用於刷新緩衝區的特殊值std::char_traits<char>::eof()來調用)。由於沒有緩衝區,每個角色都會調用overflow()。所有這些功能的作用是查看是否需要寫入前綴,如果是,則寫入prefix。如果寫入換行'\n',則該函數會記住如果寫入另一個字符,則需要編寫prefix。然後它只是將字符的寫入轉發給基礎流緩衝區。

virtual功能sync()用於同步流與其外部表示。對於保留緩衝區的流緩衝區,它確保寫入任何緩衝區。由於prefixbuf並不真正保留一個緩衝區,所以它只需要通過調用pubsync()sync()請求委託給基礎流緩衝區。

+0

我不知道這些方法(同步和溢出),你知道一個很好的教程嗎? – volperossa 2014-12-06 21:21:14

+1

IOStreams庫在Nicolai Josuttis的「C++標準庫」中進行了解釋,很明顯,解釋是_excellent_(儘管我特別寫了創建流緩衝區的原始版本)。還有Angelika Langer和Klaus Kreft的「IOStreams and Locales」,但這是一本專門介紹I/O的書。否則,如果您在Google上查看「streambuf」和「James Kanze」或「Dietmar Kuehl」,則應該找到大量資源。 – 2014-12-06 21:25:38

0

您的輸出運算符正在返回對基類(std::ostream)的引用,這意味着下一次調用operator <<將不會使用您的實現。

改變你的輸出運營商這應該解決您的問題:

template <typename T> 
parallel_cout& operator<< (const T& val) 
{ 
    out << "prefix " << val; 
    return *this; 
} 
3

正如迪特馬爾說,你要的是你自己的流緩衝,某事,這一般順序:

#include <streambuf> 
#include <iostream> 

class prefixer: public std::streambuf { 
public: 
    prefixer(std::streambuf* s): sbuf(s) {} 
    ~prefixer() { overflow('\n'); } 
private: 
    typedef std::basic_string<char_type> string; 

    int_type overflow(int_type c) { 

     if (traits_type::eq_int_type(traits_type::eof(), c)) 
      return traits_type::not_eof(c); 
     switch (c) { 
     case '\n': 
     case '\r': { 
      prefix = "[FIX]"; 
      buffer += c; 
      if (buffer.size() > 1) 
       sbuf->sputn(prefix.c_str(), prefix.size()); 
      int_type rc = sbuf->sputn(buffer.c_str(), buffer.size()); 
      buffer.clear(); 
      return rc; 
     } 
     default: 
      buffer += c; 
      return c; 
     } 
    } 

    std::string prefix; 
    std::streambuf* sbuf; 
    string buffer; 
}; 

要使用這個,你從其他流緩衝創建它的一個實例(即定義了它要寫入),然後(通常)使用這個streambuf創建一個ostream。然後,您可以寫入該ostream,並將您的前綴寫入每行輸出。例如:

int main() { 
    prefixer buf(std::cout.rdbuf()); 

    std::ostream out(&buf); 

    out << "Test\n"; 
} 
+0

不錯的努力,雖然我更喜歡我的版本:我認爲它更簡單,另一個它不會在最後一個換行符後創建尾隨前綴。它也是第一行的前綴,沒有虛假的換行符。最後,它通過刷新到底層流緩衝區。我意識到你的版本是緩衝的,但不會刷新'sync()'這會造成一些傷害。儘管我認識到不緩衝流緩衝區會帶來潛在的性能問題,但我的版本並未進行簡化。 – 2014-12-06 21:22:05

1

或者,您可以通過語法更改編寫函數。

template<typename T> 
void print(T content) 
{ 
    cout<<whatever your prefix is; 
    cout<<content; 
}