【问题标题】:redirect std::cout to a custom writer将 std::cout 重定向到自定义编写器
【发布时间】:2010-10-06 16:35:02
【问题描述】:

我想使用来自Mr-Edd's iostreams article 的这个 sn-p 在某处打印 std::clog。

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

int main()
{
    std::ostringstream oss;

    // Make clog use the buffer from oss
    std::streambuf *former_buff =
        std::clog.rdbuf(oss.rdbuf());

    std::clog << "This will appear in oss!" << std::flush;

    std::cout << oss.str() << '\\n';

    // Give clog back its previous buffer
    std::clog.rdbuf(former_buff);

    return 0;
}

所以,在主循环中,我会做类似的事情

while (! oss.eof())
{
    //add to window text somewhere
}

这是ostringstream docs,但我无法理解执行此操作的最佳方法。我有一个显示文本的方法,我只想用 ostringstream 中的任何数据调用它。

将发送到 std::clog 的任何内容重定向到我选择的方法的最简单/最佳方法是什么?是如上所述,并填写 while !eof 部分(不确定如何),还是有更好的方法,比如在某处重载一些调用我的方法的“提交”运算符?我正在寻找快速和简单的方法,我真的不想像文章那样开始使用 boost iostreams 来定义接收器之类的东西 - 那东西让我头疼。

【问题讨论】:

  • 您能更清楚您的问题是什么吗?

标签: c++ stream stringstream iostream streambuf


【解决方案1】:

我鼓励您查看Boost.IOStreams。它似乎非常适合您的用例,而且使用起来非常简单:

#include <boost/iostreams/concepts.hpp> 
#include <boost/iostreams/stream_buffer.hpp>
#include <iostream>

namespace bio = boost::iostreams;

class MySink : public bio::sink
{
public:
    std::streamsize write(const char* s, std::streamsize n)
    {
        //Do whatever you want with s
        //...
        return n;
    }
};

int main()
{
    bio::stream_buffer<MySink> sb;
    sb.open(MySink());
    std::streambuf * oldbuf = std::clog.rdbuf(&sb);
    std::clog << "hello, world" << std::endl;
    std::clog.rdbuf(oldbuf);
    return 0;
}

【讨论】:

  • 您的 sn-p 在一个空白项目中工作。我将您的接收器复制粘贴到我的真实项目中,并将您的 main 复制到某个地方的 init 方法中,没有更改任何内容,并且我收到了几页 boost 废话错误。
  • (在这一行:bio::stream_buffer sb;)
  • 我很乐意提供帮助,但需要有关您看到的错误的更多信息。 cmets 严格的大小限制在这里并不实用,因此我建议您将代码及其生成的错误发布在 Boost 用户列表中。 boost.org/community/groups.html#users
【解决方案2】:

认为您想从 ostream 中提取不为空的文本。你可以这样做:

std::string s = oss.str();
if(!s.empty()) {
    // output s here
    oss.str(""); // set oss to contain the empty string
}

如果这不是你想要的,请告诉我。

当然,更好的解决方案是去掉中间人,让一个新的 streambuf 去你真正想要的任何地方,以后不需要再探查。像这样的东西(注意,这对每个字符都有效,但在 streambufs 中也有很多缓冲选项):

class outbuf : public std::streambuf {
public:
    outbuf() {
        // no buffering, overflow on every char
        setp(0, 0);
    }

    virtual int_type overflow(int_type c = traits_type::eof()) {
        // add the char to wherever you want it, for example:
        // DebugConsole.setText(DebugControl.text() + c);
        return c;
    }
};

int main() {
    // set std::cout to use my custom streambuf
    outbuf ob;
    std::streambuf *sb = std::cout.rdbuf(&ob);

    // do some work here

    // make sure to restore the original so we don't get a crash on close!
    std::cout.rdbuf(sb);
    return 0;

}

【讨论】:

  • 我没想到它会这么简单,但我会试一试。
【解决方案3】:

我需要从第三方库获取输出到 std::cout 和 std::cerr 并使用 log4cxx 记录它们,并且仍然保留原始输出。

这就是我想出的。这很简单:

  • 我将 ostream 的旧缓冲区(如 std::cout)替换为我自己的类,以便访问写入它的内容。

  • 我还使用旧缓冲区创建了一个新的 std::ostream 对象,以便我可以继续将输出发送到我的控制台,除了将其发送到我的记录器。我觉得这很方便。

代码:

class intercept_stream : public std::streambuf{
public:
    intercept_stream(std::ostream& stream, char const* logger):
      _logger(log4cxx::Logger::getLogger(logger)),
      _orgstream(stream),
      _newstream(NULL)
    {
        //Swap the the old buffer in ostream with this buffer.
        _orgbuf=_orgstream.rdbuf(this);
        //Create a new ostream that we set the old buffer in
        boost::scoped_ptr<std::ostream> os(new std::ostream(_orgbuf));
        _newstream.swap(os);
    }
    ~intercept_stream(){
        _orgstream.rdbuf(_orgbuf);//Restore old buffer
    }
protected:
    virtual streamsize xsputn(const char *msg, streamsize count){
        //Output to new stream with old buffer (to e.g. screen [std::cout])
        _newstream->write(msg, count);
        //Output to log4cxx logger
        std::string s(msg,count);
        if (_logger->isInfoEnabled()) {
            _logger->forcedLog(::log4cxx::Level::getInfo(), s, LOG4CXX_LOCATION); 
        }
        return count;
    }
private:
    log4cxx::LoggerPtr _logger;
    std::streambuf*    _orgbuf;
    std::ostream&      _orgstream;
    boost::scoped_ptr<std::ostream>  _newstream;
};

然后使用它:

std::cout << "This will just go to my console"<<std::endl;
intercept_stream* intercepter = new intercept_stream(std::cout, "cout");
std::cout << "This will end up in both console and my log4cxx logfile, yay!" << std::endl;

【讨论】:

  • 可以通过在 isInfoEnabled 检查中移动 std::string 构造来获得一些性能。
  • 这很好用!我只更改了 intercept_stream 以采用 std::string 而不是 char const* 因为 getLogger 采用字符串并根据 Rhys Ulerich 的建议将字符串构造移动到 isInfoEnabled 检查中。
【解决方案4】:

对于 log4cxx 示例,您必须覆盖 overflow() 和 sync() 否则在接收到第一个流后总是设置坏位。

见: http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/fd9d973282e0a402/a872eaedb142debc

InterceptStream::int_type InterceptStream::overflow(int_type c)
{
    if(!traits_type::eq_int_type(c, traits_type::eof()))
    {
        char_type const t = traits_type::to_char_type(c);
        this->xsputn(&t, 1);
    }
    return !traits_type::eof();
}

int InterceptStream::sync()
{
    return 0;
}

【讨论】:

    【解决方案5】:

    如果您只想获取 ostringstream 的内容,请使用它的 str() 成员。例如:

    string s = oss.str();    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 2012-06-16
      • 1970-01-01
      相关资源
      最近更新 更多