在std :: cout flush事件上捕获和引发事件

时间:2015-06-21 23:38:07

标签: c++ c++11 iostream

我试图编写一个范围对象来重定向std::cout的输出,并在刷新底层缓冲区时调用一个函数。

我的实施大量借鉴以下SO答案:

c++ execute function any time a stream is written to

我有部分工作,但只有当我明确地在flush上调用std::cout时才会调用回调函数。但是,每当任何写入流时,我希望它调用回调函数。

注意:我正在编译aginst MSVC ++ 11。

struct stream_redirect
{
    stream_redirect(std::ostream& stream, std::streambuf* rdbuf) :
        stream(stream),
        rdbuf_old(stream.rdbuf())
    {
        stream.set_rdbuf(rdbuf);
    }

    ~stream_redirect()
    {
        stream.set_rdbuf(rdbuf_old);
    }

private:
    stream_redirect(const stream_redirect&) = delete;
    stream_redirect& operator = (const stream_redirect&) = delete;

    std::ostream& stream;
    std::streambuf* rdbuf_old;
};

struct functionbuf : public std::streambuf
{
    typedef std::function<void(std::string)> function_type;

    functionbuf(const function_type& function)
        : function(function)
    {
        setp(buffer, buffer + sizeof(buffer) - 1);
    }

private:
    char buffer[1024];
    function_type function;

    virtual int_type overflow(int_type c) override
    {
        if (!traits_type::eq_int_type(c, traits_type::eof()))
        {
            *this->pptr() = traits_type::to_char_type(c);
            pbump(1);
        }

        return sync() ? traits_type::not_eof(c) : traits_type::eof();
    }

    virtual int_type sync() override
    {
        if (pbase() != pptr())
        {
            function(std::string(pbase(), pptr()));

            setp(pbase(), epptr());
        }

        return 0;
    }
};

struct ofunctionstream :
    private virtual functionbuf,
    public std::ostream
{
    ofunctionstream(const function_type& function) :
        functionbuf(function),
        std::ostream(static_cast<std::streambuf*>(this))
    {
        setf(std::ios_base::unitbuf);
    }
};

现在是一个用法示例:

void callback(std::string string)
{
    printf("callback(%s)\n", string.c_str());
}

int main()
{
    ofunctionstream fs(&callback);
    stream_redirect sr(std::cout, fs.rdbuf());

    printf("sending string to cout...");
    std::cout << "hello!";
    printf("string sent to cout");

    //this is necessary to 
    printf("flushing cout...");
    std::cout.flush();
    printf("cout flushed");
}

我得到以下输出:

sending string to cout...
string sent to cout
flushing cout...
callback(hello!)
cout flushed

同样,我希望在调用std::cout << "hello!";后立即调用该回调函数。我认为这会发生,因为我在其setf(std::ios_base::unitbuf)构造函数中的ofunctionstream对象上调用import pygame, sys from pygame.locals import * #here is the error >>> Traceback (most recent call last): File "C:/Python34/game.py", line 2, in <module> from pygame.locals import * ImportError: No module named 'pygame.locals' http://en.cppreference.com/w/cpp/io/manip/unitbuf)。

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:3)

如果你检查你正在使用的回调是如何工作的,那么它可以通过继承std::streambuf和覆盖overflow()来实现。这一点很重要。

引用C ++标准库的相关部分:

  

27.6.3.2.5放置区域[streambuf.pub.put]

     

int_type sputc(char_type c);

     

返回:如果输出序列写入位置不可用,   返回溢出(traits :: to_int_- type(c))。否则,在商店c   输出序列的下一个指针,递增指针,和   返回traits :: to_int_type(c)

std::ostream,a.k.a。格式化输出,使用sputc()写入流缓冲区。因此,调用唯一的时间overflow()是输出缓冲区耗尽的时间。或明确地通过std::flush

所以,不幸的是,你的选择有限。要么处理当前行为,要么陪审std::streambuf子类根本没有写缓冲区,以便每个字符最终通过sputc()写入受到overflow()的惩罚,并调用你的子类实现。

不幸的是,流操作在每次格式化操作后都不执行任何显式操作,可以由std::streambuf拦截。他们只需通过sputc()一次编写格式化的输出,一次一个字符,这样输出就会被收集到streambuf的写缓冲区中,只有当它被填满时才会被清除,或者明确使用std::flush时。

那就是它的运作方式。