У меня есть простая программа с графическим интерфейсом, которая использует пользовательский stringstream
перенаправить вывод из консоли в текстовое поле в графическом интерфейсе (при некоторых обстоятельствах). В настоящее время. окно перерисовывается каждый раз, когда я нажимаю клавишу ввода, но возможно, что выходные данные могут быть сгенерированы в другое время. Есть ли способ зарегистрировать функцию с stringstream
который выполняется каждый раз, когда <<
оператор используется в потоке?
НОТА
Я должен был указать, что я не могу использовать C ++ 11 в своем решении. машины, на которых это будет скомпилировано и запущено, не будут иметь доступного c ++ 11.
Лично я бы не использовал std::ostringstream
(или даже std::stringstream
) для этого вообще! Вместо этого я бы создал свой собственный потоковый буфер, заботясь об отправке данных в графический интерфейс. То есть я бы перезаписал std::streambuf::overflow()
а также std::streambuf::sync()
отправить текущие данные в графический интерфейс. Чтобы также убедиться, что любой вывод отправляется немедленно, я бы настроил std::ostream
иметь std::ios_base::unitbuf
задавать. На самом деле отправка изменений в функцию довольно проста, то есть я реализую это:
#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
где действительно подразумевается флеш).
Для этого вы должны создать свой собственный класс streambuf. Классы streambuf представляют устройства ввода-вывода, и каждый из них заботится о различных проблемах, специфичных для такого типа устройств. Стандарт определяет streambuf для файлов и другой для строк. Сетевой доступ будет использовать другой, и вывод на GUI также должен быть представлен как другой тип устройства, если вы вообще собираетесь использовать потоки.
Написание подходящего класса streambuf не тривиально и кажется неясным, но есть ресурсы. Стандартная библиотека C ++ — учебное пособие и справочник имеет небольшой раздел по этому вопросу. Стандартные C ++ IOStreams и локали: Расширенное руководство программиста и справочник предоставляет подробную информацию. Поиск по subclassing basic_streambuf
также появятся некоторые бесплатные ресурсы онлайн.
Если вы еще этого не сделали, можете ли вы извлечь подкласс из stringstream и перегрузить его оператор вставки потока для генерации событий?
псевдокод:
class AlertingStream : public stringstream
{
ostream& operator << (type)
{
for (each listener in listeners)
{
listener.notify();
}
perform insertion;
return *this;
}
}