secuencias - Función de ejecución c++ cada vez que se escribe una secuencia en
salto de renglon en c (3)
Tengo un programa de GUI simple que usa un stringstream
personalizado para redirigir el resultado de la consola a un campo de texto en la GUI (bajo algunas circunstancias). actualmente. la ventana se vuelve a dibujar cada vez que pulso enter, pero es posible que la salida se pueda generar en otros momentos. ¿Hay alguna forma de registrar una función con el stringstream
que se ejecuta cada vez que se utiliza el operador <<
en la transmisión?
NOTA
Debería haber señalado que no puedo usar C ++ 11 en mi solución. las máquinas en las que se compilará y ejecutará no tendrán c ++ 11 disponible.
Personalmente, ¡no usaría std::ostringstream
(o incluso std::stringstream
) para esto! En cambio, crearía mi propio búfer de transmisión teniendo cuidado de enviar los datos a la GUI. Es decir, sobrescribiría std::streambuf::overflow()
y std::streambuf::sync()
para enviar los datos actuales a la GUI. Para asegurarme de que cualquier salida se envíe inmediatamente, configuré un std::ostream
para tener el conjunto std::ios_base::unitbuf
. En realidad, enviar los cambios a una función es bastante simple, es decir, implementaré esto:
#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;
}
Un buen fragmento del código anterior en realidad no está relacionado con la tarea en cuestión: implementa una versión primitiva de std::function<void(std::string)>
en caso de que C ++ 2011 no se pueda utilizar.
Si no quiere tantas llamadas, puede desactivar std::ios_base::unitbuf
y solo enviar los datos al descargar la secuencia, por ejemplo, usando std::flush
(sí, sé de std::endl
pero desafortunadamente, se usa indebidamente para recomendar encarecidamente deshacerse de él y utilizar std::flush
donde realmente se quiere decir un color).
Para hacer esto, debes crear tu propia clase streambuf. Las clases streambuf representan dispositivos IO y cada uno se ocupa de los diversos problemas específicos de ese tipo de dispositivo. El estándar define un streambuf para archivos y otro para cadenas. El acceso a la red usaría otro, y la salida a una GUI también debería representarse como otro tipo de dispositivo si va a usar streams en absoluto.
Escribir una clase de streambuf adecuada no es trivial y parece ser algo oscuro, pero hay recursos por ahí. La biblioteca estándar de C ++ - Un tutorial y referencia tiene una pequeña sección sobre esto. Estándar C ++ IOStreams y Locales: Advanced Programmer''s Guide and Reference proporciona información detallada. Una búsqueda de subclassing basic_streambuf
también generará algunos recursos gratuitos en línea.
Si aún no lo ha hecho, ¿puede derivar una subclase de secuencia de cadenas y sobrecargar su operador de inserción de secuencias para generar eventos?
Pseudocódigo:
class AlertingStream : public stringstream
{
ostream& operator << (type)
{
for (each listener in listeners)
{
listener.notify();
}
perform insertion;
return *this;
}
}