2014-02-26 50 views
1

原始問題是:如何在發生換行符後自動插入某些內容到流中。插入應該只在事後插入(手動)插入流時纔會發生。C++ Stream:在換行符後面插入字符串

以下是更詳細的解釋。


對於練習我正在寫自己的記錄器類。基本的日誌記錄功能由這段代碼給出。

class Logger { 
public: 
    static std::ostream & log(LogLevels::LogLevel level); 
}; 

// Use it: 
Logger::log(LogLevels::WARNING) << "Something serious happened!" << std::endl; 

的示例打印像

 
[ WARNING: 0 ] Something serious happened! 

我想延長這一功能,使得一個新行插入流之後所有調試消息由記錄頭的寬度縮進直到Logger::log再次被調用。

// Example (1) 
Logger::log(LogLevels::WARNING) << "Something happened!" 
            << std::endl 
            << "Some More information: " 
            << 42 
            << " (Still on the same line)" 
            << std::endl; 

Logger::log(LogLevels::INFO) << "Merely a status code" 
           << std::endl 
           << "Which reads: " 
           << 21 
           << " (Also on the same line)" 
           << std::endl; 

// Example (2) 
std::ostream & os = Logger::log(LogLevels::WARNING); 
os << "First line" 
    << std::endl; 

os << "Second line" 
    << std::endl; 

// Example (3) 
// [...] 
// Some code is executed 
Logger::log(LogLevels::WARNING) << "A new error" 
            << std::endl 
            << "The code strikes back" 
            << std::endl 
            << "The return of the bugs" 
            << std::endl ; 

這將產生:

 
[ WARNING: 0 ] Something hapened! 
       Some More information: 42 (Still on the same line) 
[ INFO: 1 ] Merely a status code 
      Which reads: 21 (Also on the same line) 
[ WARNING: 2 ] First line 
       Second line 
// [...] 
[ WARNING: 99998 ] A new error 
        The code strikes back 
        The return of the bugs 

這種行爲可以實現,如果是這樣,這是如何最好地展示一個例子來說明?

+1

http://kuhllib.com/2012/01/14/stop-excessive-use-of-stdendl/ – BoBTFish

+0

這似乎不是一個非常直觀的界面。 –

+0

@BoBTFish's/std :: endl /'\ n'/ g'我的問題不是關於使用'std :: endl',我很清楚速度的不同。不過,我認爲在例子中使用'std :: endl'這樣的代碼使得它更具可讀性。 – elemakil

回答

1

我覺得更直觀的界面將是:

Logger::log(LogLevels::WARNING) << "Something happened!" 
    << Logger::tabbedLine 
    << "More Info"; 

這樣一來,用戶仍然可以使用'\n'std::endl才能到下一行的開始,或使用Logger::tabbedLine(一個不好的名字,我承認)實現你想要的。

您可以繼承std::ostream的子類,併爲operator<<添加一個過載,它接收一個特殊的類tabbedLine是這種類型的靜態實例。您需要在您創建的流子類中「記住」所需的填充。

Logger::log將返回此ostream子類的實例。

+0

我有一個類似的解決方案(沒有'std :: ostream的子類','Logger'有一個靜態'std :: string'成員'pad_newline',它插入適當的填充)。但是,使用這種解決方案,用戶必須記住要爲要插入的最後一個項目(沒有填充的項目)選擇正確的換行符,對嗎? – elemakil

+0

作爲一般規則,這是一個你想要使用宏的地方,以便自動插入'__FILE__'和'__LINE__'。 –

+0

@elemakil - 通常在記錄器中,用戶不會在消息之後添加新行 - 記錄器應該自動執行此操作(所以'Logger :: log(INFO)<<「Started」;'將添加一個換行符)。 – Asaf

2

只需在ostream和 最終目標流緩衝區之間插入過濾streambuf即可。像下面這樣的東西應該 的伎倆:

class HeaderInserter : public std::streambuf 
{ 
    std::streambuf* myDest; 
    bool myIsAtStartOfLine; 
protected: 
    int overflow(int ch) override 
    { 
     int retval = 0; 
     if (ch != traits_type::eof()) { 
      if (myIsAtStartOfLine) { 
       std::string header = getHeader(); 
       myDest->sputn(header.data(), header.size()); 
      } 
      retval = myDest->sputc(ch); 
      myIsAtStartOfLine = ch == '\n'; 
     } 
     return retval; 
    } 
public: 
    HeaderInserter(std::streambuf* dest) 
     : myDest(dest) 
     , myIsAtStartOfLine(true) 
    { 
    } 
}; 

創建的其中之一,有一個指向您的最終目的地 (std::cerr.rdbuf(),或者你打開一個std::filebuf), 然後使用std::ostream這點到它。

我在我自己的記錄器類中使用它;我添加了附加功能 來開始和完成一個新的記錄器記錄: 開始記錄後的第一個輸出輸出一個時間戳,加上我已經傳輸的__FILE____LINE__;以下頭文件是 只是任意數量的空格,以便縮進。完成功能還將確保記錄以 a '\n'結束,並被刷新。

最後,一些更普遍的意見:第一,你不希望 客戶端的代碼來調用Logger::log,而是一些宏,在 以自動傳遞__FILE____LINE__到所創建的 實際的對象。對象本身應該是臨時的,它的析構函數調用上面的完成例程。 最後,如果你在多線程環境中,你可能想要將streambuf與 無限制的緩衝區(例如std::vector<char>)相關聯,然後 忽略來自用戶的任何刷新,然後執行任何操作當調用完成 例程時,需要原子地寫入整個緩衝區所需的 ,當然,確保每個線程都具有其所有上述組件的自己的實例。 (如果你在 在Unix平臺,例如,POSIX函數write是 保證原子,所以你不需要任何鎖。)

1

你可以寫一個自己的流,流緩衝和操縱者:

#include <iostream> 
#include <iomanip> 
#include <sstream> 

// LogBuffer 
// ============================================================================ 

class LogBuffer : public std::streambuf 
{ 
    // Types 
    // ===== 

    public: 
    typedef typename std::streambuf buffer_type; 
    typedef typename buffer_type::char_type char_type; 
    typedef typename buffer_type::traits_type traits_type; 
    typedef typename buffer_type::int_type int_type; 
    typedef typename buffer_type::pos_type pos_type; 
    typedef typename buffer_type::off_type off_type; 

    // Construction/Destructiion 
    // ========================= 

    public: 
    LogBuffer(buffer_type& buffer) 
    : m_buffer(buffer), m_indent(0), m_warning_count(0) 
    {} 

    public: 
    ~LogBuffer() { 
     m_buffer.pubsync(); 
    } 

    private: 
    LogBuffer(LogBuffer const&); // No Copy. 
    LogBuffer& operator=(LogBuffer const&); // No Copy. 


    // Functionality 
    // ============= 

    public: 
    bool write(const std::string& s) { 
     return m_buffer.sputn(s.data(), s.size()) == std::streamsize(s.size()); 
    } 

    bool warning() { 
     std::ostringstream out; 
     out << "[ WARNING: " << m_warning_count++ << "] "; 
     m_indent = out.str().size(); 
     return write(out.str()); 
    } 
    bool insert_indent() { 
     std::ostringstream out; 
     out << std::setw(m_indent) << ""; 
     return write(out.str()); 
    } 

    // Virtuell 
    // ======== 

    protected: 
    std::streamsize xsputn(const char_type* s, std::streamsize n) { 
     return m_buffer.sputn(s, n); 
    } 

    int_type overflow(int_type ch) { 
     if(ch == traits_type::eof()) return traits_type::eof(); 
     else return m_buffer.sputc(traits_type::to_char_type(ch)); 
    } 

    int sync() { 
     return m_buffer.pubsync(); 
    } 

    private: 
    buffer_type& m_buffer; 
    unsigned m_indent; 
    unsigned m_warning_count; 
}; 



// LogStream 
// ============================================================================ 

class LogStream : public std::ostream 
{ 
    // Types 
    // ===== 

    private: 
    typedef std::ostream Base; 
    typedef LogBuffer buffer_type; 

    public: 
    typedef std::ostream stream_type; 
    typedef typename Base::char_type char_type; 
    typedef typename Base::traits_type traits_type; 
    typedef typename Base::int_type int_type; 
    typedef typename Base::pos_type pos_type; 
    typedef typename Base::off_type off_type; 

    // Construction 
    // ============ 

    public: 
    LogStream() 
    : Base(&m_buffer), m_buffer(*std::clog.rdbuf()) 
    {} 

    LogStream(stream_type& stream) 
    : Base(&m_buffer), m_buffer(*stream.rdbuf()) 
    {} 

    private: 
    LogStream(const LogStream&); // No copy. 
    const LogStream& operator = (const LogStream&); // No copy. 

    private: 
    buffer_type m_buffer; 
}; 



// Manipulator 
// =========== 

std::ostream& log_warning(std::ostream& stream) { 
    LogBuffer* buffer = dynamic_cast<LogBuffer*>(stream.rdbuf()); 
    if(buffer) { 
     if(! buffer->warning()) 
      stream.setstate(std::ios_base::failbit); 
    } 
    return stream; 
} 


std::ostream& log_indent(std::ostream& stream) { 
    LogBuffer* buffer = dynamic_cast<LogBuffer*>(stream.rdbuf()); 
    if(buffer) { 
     if(! buffer->insert_indent()) 
      stream.setstate(std::ios_base::failbit); 
    } 
    return stream; 
} 


int main() { 
    LogStream log; 
    log << log_warning 
     << "First\n" 
     << log_indent 
     << "Second\n" 
     << std::flush; 
} 
相關問題