4

我有一个简单的 GUI 程序,它使用自定义stringstream将控制台的输出重定向到 GUI 中的文本字段(在某些情况下)。目前。每当我按 Enter 键时,窗口都会重绘,但有可能在其他时间生成输出。有没有办法注册一个函数,stringstream每次<<在流上使用运算符时都会执行该函数?

笔记

我应该指出我不能在我的解决方案中使用 C++11。将在其上编译和运行它的机器将没有可用的 c++11。

4

3 回答 3

8

就个人而言,我根本不会使用std::ostringstream(甚至是std::stringstream)!相反,我会创建自己的流缓冲区,负责将数据发送到 GUI。也就是说,我将覆盖当前数据std::streambuf::overflow()并将std::streambuf::sync()其发送到 GUI。为了确保立即发送任何输出,我设置了一个std::ostreamto have std::ios_base::unitbufset。实际上,将更改发送到函数非常简单,即,我将实现:

#include <streambuf>
#include <ostream>
#include <functional>
#include <string>
#include <memory>
#include <iostream> // only for testing...

#if HAS_FUNCTION
typedef std::function<void(std::string)> function_type;
#else
class function_type
{
private:
    struct base {
        virtual ~base() {}
        virtual base* clone() const = 0;
        virtual void  call(std::string const&) = 0;
    };
    template <typename Function>
    struct concrete
        : base {
        Function d_function;
        concrete(Function function)
            : d_function(function) {
        }
        base* clone() const { return new concrete<Function>(this->d_function); }
        void  call(std::string const& value) { this->d_function(value); }
    };
    std::auto_ptr<base> d_function;
public:
    template <typename Function>
    function_type(Function function)
        : d_function(new concrete<Function>(function)) {
    }
    function_type(function_type const& other)
        : d_function(other.d_function->clone()) {
    }
    function_type& operator= (function_type other) {
        this->swap(other);
        return *this;
    }
    ~function_type() {}
    void swap(function_type& other) {
        std::swap(this->d_function, other.d_function);
    }
    void operator()(std::string const& value) {
        this->d_function->call(value);
    }
};
#endif

class functionbuf
    : public std::streambuf {
private:
    typedef std::streambuf::traits_type traits_type;
    function_type d_function;
    char          d_buffer[1024];
    int overflow(int c) {
        if (!traits_type::eq_int_type(c, traits_type::eof())) {
            *this->pptr() = traits_type::to_char_type(c);
            this->pbump(1);
        }
        return this->sync()? traits_type::not_eof(c): traits_type::eof();
    }
    int sync() {
        if (this->pbase() != this->pptr()) {
            this->d_function(std::string(this->pbase(), this->pptr()));
            this->setp(this->pbase(), this->epptr());
        }
        return 0;
    }
public:
    functionbuf(function_type const& function)
        : d_function(function) {
        this->setp(this->d_buffer, this->d_buffer + sizeof(this->d_buffer) - 1);
    }
};

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

void some_function(std::string const& value) {
    std::cout << "some_function(" << value << ")\n";
}

int main() {
    ofunctionstream out(&some_function);
    out << "hello" << ',' << " world: " << 42 << "\n";
    out << std::nounitbuf << "not" << " as " << "many" << " calls\n" << std::flush;
}

上述代码的相当一部分实际上与手头的任务无关:它实现了一个原始版本,std::function<void(std::string)>以防 C++2011 无法使用。

如果您不想要太多的调用,您可以关闭std::ios_base::unitbuf并仅在刷新流时发送数据,例如使用std::flush(是的,我知道std::endl但不幸的是它通常被滥用,我强烈建议摆脱它并使用std::flush真正意味着冲洗的地方)。

于 2012-10-10T19:48:14.870 回答
4

为此,您应该创建自己的 streambuf 类。streambuf 类代表 IO 设备,每个类都处理特定于该类型设备的各种问题。该标准为文件定义了一个streambuf,为字符串定义了另一个。网络访问将使用另一种,如果您要使用流,则输出到 GUI 也应该表示为另一种设备。

编写适当的 streambuf 类并非易事,而且似乎有点晦涩难懂,但是那里有资源。C++ 标准库 - 教程和参考对此有一小部分。标准 C++ IOStreams 和语言环境:高级程序员指南和参考提供了深入的信息。搜索subclassing basic_streambuf也会在网上找到一些免费资源。

于 2012-10-10T19:40:00.303 回答
1

如果您还没有,您能否从 stringstream 派生一个子类并重载其流插入运算符以生成事件?

伪代码:

class AlertingStream : public stringstream
{
    ostream& operator << (type)
    {
        for (each listener in listeners)
        {
            listener.notify();
        }
        perform insertion;
        return *this;
    }
}
于 2012-10-10T19:29:09.853 回答