【问题标题】:Custom C++ manipulator problem自定义 C++ 操纵器问题
【发布时间】:2009-11-16 04:13:38
【问题描述】:

我正在尝试在我的日志记录类中实现我自己的流操纵器。它基本上是改变标志状态的端线操纵器。但是,当我尝试使用它时,我会得到:

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;

(log是指向Log对象的指针)

有什么想法吗?我怀疑它与机械手实际上在课堂内的事实有某种联系,但这只是我的猜测......

干杯,

汤姆

编辑:由于格式限制,将其放在这里而不是评论。 我尝试实现我的 streambuf,它运行良好,但有一个例外:当我尝试打开 filebuf 进行追加时,它失败了。输出效果很好,只是由于某些未知原因而没有附加。如果我尝试直接使用 ofstream 和 append 它可以工作。知道为什么吗? – 作品:

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

正确附加“测试”。

没用:

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

抛出异常,如果我将 openmode 设置为 out 则它可以工作..

干杯

【问题讨论】:

  • 在这种情况下,由于您的后续问题基本上与原始问题完全无关,因此最好将其放在其他地方,以免其他人可能会在稍后出现谷歌搜索什么的。更重要的是,它会让你的问题得到更多关注:)

标签: c++ stream manipulators


【解决方案1】:

定义了operator&lt;&lt;(ostream &amp;, ostream &amp;(*)(ostream&amp;)),但没有定义operator&lt;&lt;(ostream &amp;, ostream &amp;(Log::*)(ostream&amp;))。也就是说,如果操纵器是一个普通(非成员)函数,它会工作,但因为它依赖于Log 的实例,所以普通重载将不起作用。

要解决此问题,您可能需要让 log-&gt;endl 成为辅助对象的实例,并在使用 operator&lt;&lt; 推送时调用相应的代码。

像这样:

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) {
  // ...
}

【讨论】:

    【解决方案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::ostreams。毕竟,std::cout &lt;&lt; Log-&gt;endl() 是什么意思?因此,您需要创建一个从 std::ostream 派生的新流类型(我在这里省略了它,但假设它被命名为 LogStream)。
    2. endl 本身实际上并没有做任何事情;所有的工作都在operator&lt;&lt;。它的唯一目的是让operator&lt;&lt; 重载运行。

    也就是说,如果可以避免的话,你不应该定义新的操纵器和流类,因为它变得复杂 :) 你能做你需要的只使用 std::endl 并包装一个 ostream您自己的自定义streambuf?这就是 C++ IO 库的用途。

    【讨论】:

    • 实际上,std::endl 确实有效。但是,它是间接这样做的。首先,通过将其作为参数传递,调用正确的operator&lt;&lt; 重载。然后这个重载依次调用std::endl()
    • 谢谢大家的回答。我可以看出我完全错了。我试图实现的是知道流是否被刷新,以便我可以在日志中写入“标题”(时间、日志级别等)。我一直在寻找一些简单的方法来做到这一点。所以你说最好的方法是写我自己的streambuf(不是ostream)?这么简单的事情是不是太低级了?干杯
    • streambufostream 之间的区别在于 streambuf 只是获取未格式化的原始字节,而 ostream 处理格式化而不过度关注底层流缓冲区将如何获得该输出.我认为streambuf 正是您想要的位置,仅仅是因为您真的不需要采取任何特殊的格式化操作。您只需要在每个'\n' 之后插入日志头数据,这可以在streambuf 级别轻松完成。
    • 好的,我尝试实现我的 streambuf,它运行良好,但有一个例外:当我尝试打开 filebuf 进行追加时,它失败了。输出效果很好,只是由于某些未知原因而没有附加。如果我尝试直接使用 ofstream 和 append 它可以工作。知道为什么吗?
    • 作品:std::ofstream 测试; test.open("somefile", std::ios_base::app); if (!test) throw LogIoEx("Cannon open file for logging");测试 open("somefile", std::ios_base::app)) throw LogIoEx("Cannon open file for logging");抛出异常,如果我将 openmode 设置为 out 那么它可以工作......
    【解决方案3】:

    试试这个:

    #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();
    }
    

    【讨论】:

    • 干杯,这看起来很接近,但我可以看到我错过了重点,我现在想正确地做:))
    猜你喜欢
    • 2010-10-06
    • 1970-01-01
    • 2015-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-11
    • 1970-01-01
    相关资源
    最近更新 更多