2009-11-16 57 views
0

我試圖在我的日誌記錄類中實現我自己的流操縱器。它基本上是改變旗子狀態的端線操縱器。然而,當我嘗試使用它,我會得到:自定義C++操縱器問題

ftypes.cpp:57: error: no match for ‘operator<<’ in ‘log->Log::debug() << log->Log::endl’ 
/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/ostream.tcc:67: note: candidates are: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>& (*)(std::basic_ostream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>] 
/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/ostream.tcc:78: note:     std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ios<_CharT, _Traits>& (*)(std::basic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>] 
/usr/lib/gcc/i386-redhat-linux/4.1.2/../../../../include/c++/4.1.2/bits/ostream.tcc:90: note:     std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::ios_base& (*)(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits<char>] 

...

代碼:

class Log { 
public: 
    ... 
    std::ostream& debug() { return log(logDEBUG); } 
    std::ostream& endl(std::ostream& out);   // manipulator 
    ... 
private: 
    ... 
    std::ofstream m_logstream; 
    bool   m_newLine; 
    ... 
} 


std::ostream& Log::endl(std::ostream& out) 
{ 
    out << std::endl; 
    m_newLine = true; 
    return out; 
} 

std::ostream& Log::log(const TLogLevel level) 
{ 
    if (level > m_logLevel) return m_nullstream; 

    if (m_newLine) 
    { 
    m_logstream << timestamp() << "|" << logLevelString(level) << "|"; 
    m_newLine = false; 
    } 
    return m_logstream; 
} 

我得到的錯誤,當我試圖把它叫做:

log->debug() << "START - object created" << log->endl; 

(日誌是登錄對象的指針)

任何想法?我懷疑這是某種方式連接到一個事實,即操作者實際是在類中,但是這只是我的胡亂猜測......

乾杯,

湯姆

編輯:在這裏把這個代替,因爲評論限制格式。 我試圖實現我的streambuf,它工作得很好,但有一個例外:當我嘗試打開filebuf追加失敗。輸出很好地工作,只是附加不出於某種未知的原因。如果我嘗試直接使用ofstream和append,它會起作用。任何想法爲什麼? - Works:

std::ofstream test; 
test.open("somefile", std::ios_base::app); 
if (!test) throw LogIoEx("Cannon open file for logging"); 
test << "test" << std::endl; 

正確追加「test」。

不起作用:

std::filebuf *fbuf = new std::filebuf(); 
if (!fbuf->open("somefile", std::ios_base::app)) throw LogIoEx("Cannon open file for logging"); 

拋出異常,如果我設置用於openmode來了,然後它工作..

乾杯

+0

在這種情況下,因爲你的後續問題基本上是完全無關的原來的問題,它會是更好地把它別的地方,讓事情變得容易混淆別人誰可能稍後在谷歌搜索或其他東西。更重要的是,它會帶來更多的關注你的問題:) – bdonlan 2009-11-23 01:20:52

回答

2

那不是如何操縱工作 - 這是所有關於類型。你想要的是一樣的東西:

class Log { 
... 
struct endl_tag { /* tag struct; no members */ }; 
static const struct endl_tag endl; 
... 
LogStream &debug() { /* somehow produce a LogStream type here */ } 
} 

LogStream &operator<<(LogStream &s, const struct endl_tag &) { 
    s.m_newLine = true; 
} 

需要注意的是:

  1. 由於m_newLine是Log一部分,我們不能與通用std::ostream s工作。畢竟std::cout << Log->endl()是什麼意思?所以你需要創建一個來自std::ostream的新流類型(我已經把它留在這裏,但是假設它被命名爲LogStream)。
  2. endl實際上並沒有做任何事情;所有的工作都在operator<<。它的唯一目的是讓正確的operator<<超載運行。

這就是說,你不應該被定義新的操縱和流類,如果你能避免它,因爲它可以讓複雜的:)你可以做你需要使用剛剛std::endl什麼,並且繞一個ostream您自己定製streambuf?這就是C++ IO庫的意圖。

+0

+1建議一個子類而不是使用魔法。 – strager 2009-11-16 04:48:08

+1

其實,'std :: endl'事實上確實有效。但是,它是間接的。首先,將它作爲參數傳遞,調用正確的'operator <<'重載。然後這個超載調用'std :: endl()' – MSalters 2009-11-16 09:00:32

+0

謝謝你們的答案。我可以看到我完全錯了。我試圖實現的是知道流是否被刷新,以便我可以在日誌(時間,日誌級別等)中寫入「標題」。我正在尋找一些簡單的方法來做到這一點。所以你說最好的方法是寫我自己的streambuf(不ostream)?這樣簡單的事情不是太低級嗎? Cheers – Tom 2009-11-16 21:12:03

5

有定義的operator<<(ostream &, ostream &(*)(ostream&))但不是一個operator<<(ostream &, ostream &(Log::*)(ostream&))。也就是說,如果操縱器是普通(非成員)函數,它將工作,但由於它取決於Log的實例,所以正常的重載不起作用。

要解決此問題,您可能需要將log->endl作爲幫助程序對象的實例,並在按operator<<推送時調用相應的代碼。

像這樣:

class Log { 
    class ManipulationHelper { // bad name for the class... 
    public: 
    typedef ostream &(Log::*ManipulatorPointer)(ostream &); 

    ManipulationHelper(Log *logger, ManipulatorPointer func) : 
     logger(logger), 
     func(func) { 
    } 

    friend ostream &operator<<(ostream &stream, ManipulationHelper helper) { 
     // call func on logger 
     return (helper.logger)->*(helper.func)(stream); 
    } 

    Log *logger; 
    ManipulatorPointer func; 
    } 

    friend class ManipulationHelper; 

public: 
    // ... 

    ManipulationHelper endl; 

private: 
    // ... 

    std::ostream& make_endl(std::ostream& out); // renamed 
}; 

// ... 

Log::Log(...) { 
    // ... 
    endl(this, make_endl) { 
    // ... 
} 
1

試試這個:

#include <iostream> 

class Log 
{ 
    public: 
    class LogEndl 
    { 
     /* 
     * A class for manipulating a stream that is associated with a log. 
     */ 
     public: 
      LogEndl(Log& p) 
       :parent(p) 
      {} 
     private: 
      friend std::ostream& operator<<(std::ostream& str,Log::LogEndl const& end); 
      Log& parent; 
    }; 
    std::ostream& debug() {return std::cout;} 
    /* 
    * You are not quite using manipulators the way they are entended. 
    * But I wanted to give an example that was close to your original 
    * 
    * So return an object that has an operator << that knows what to do. 
    * To feed back info to the Log it need to keep track of who its daddy is. 
    */ 
    LogEndl  endl() {return LogEndl(*this);} 
    private: 
     friend std::ostream& operator<<(std::ostream& str,Log::LogEndl const& end); 
     bool endOfLine; 

}; 


std::ostream& operator<<(std::ostream& str,Log::LogEndl const& end) 
{ 
    // Stick stuff on the stream here. 
    str << std::endl; 

    // Make any notes you need in the log class here. 
    end.parent.endOfLine = true; 

    return str; 
}; 

int main() 
{ 
    Log  log; 

    /* 
    * Please note the use of objects rather than pointers here 
    * It may help 
    */ 
    log.debug() << "Debug " << log.endl(); 
} 
+0

乾杯,這看起來足夠接近,但我可以看到我錯過了這一點,我想現在正確地做:))) – Tom 2009-11-16 21:13:43