2009-11-16 60 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