2009-04-21 18 views
25

He estado buscando en Google y simplemente no puedo encontrar una respuesta simple para esto. Y debería ser simple, como generalmente lo es STL.Cómo heredar de std :: ostream?

Quiero definir MyOStream que hereda públicamente de std :: ostream. Digamos que quiero llamar a foo() cada vez que se escribe algo en mi transmisión.

class MyOStream : public ostream { 
public: 
    ... 
private: 
    void foo() { ... } 
} 

Entiendo que la interfaz pública de ostream no es virtual, entonces, ¿cómo se puede hacer? Quiero que los clientes puedan usar el operador < < y write() y put() en MyOStream y que utilicen la capacidad extendida de mi clase.

+3

El STL puede ser simple, pero eso es solo una parte de la biblioteca estándar de C++. La biblioteca iostreams no tiene nada que ver con (lo que fue) el STL. STL es básicamente contianers + iterators + algorithms. Iostreams, configuraciones regionales y todo eso tiene un origen completamente diferente, y en general es un dolor trabajar con él;) – jalf

Respuesta

19

No es una pregunta simple, desafortunadamente. Las clases de las que debe derivar son las clases basic_, como basic_ostream. Sin embargo, la derivación de una secuencia puede no ser la que desea, puede que desee derivar de una memoria intermedia de secuencia en su lugar, y luego utilizar esta clase para crear una instancia de una clase de secuencia existente.

Toda el área es compleja, pero hay un excelente libro sobre ella Standard C++ IOStreams and Locales, que sugiero que eche un vistazo antes de seguir adelante.

+0

Iba a buscar mi copia de este libro, pero me has ahorrado tener que hacerlo. +1 – Evan

+0

@anon No encuentro ninguna referencia a un flujo en la web. ¿Qué es? –

0

Composición, no herencia. Su clase contiene, "envuelve" un ostream &, y lo reenvía (después de llamar a foo()).

+0

Por favor, publique un código que ilustre cómo esto funcionaría con los operadores << existentes. Y tenga en cuenta que se debe llamar a foo() cada vez que se utiliza dicho operador. –

+6

La composición no siempre es la mejor solución, al igual que la herencia. ostream tiene una docena de operadores sobrecargados implementados, no se espera que nadie reescriba toda la interfaz pública de ostream solo para agregar una pequeña funcionalidad a una clase. – Michael

4

No sé si esta es la solución correcta, pero heredé de std :: ostream de esta manera. Utiliza un búfer heredado de std :: basic_streambuf y obtiene 64 caracteres a la vez (o menos si se vació) y los envía a un método putChars() genérico donde se realiza el manejo real de los datos. También demuestra cómo dar datos de usuario.

Live Example

#include <streambuf> 
#include <ostream> 
#include <iostream> 

//#define DEBUG 

class MyData 
{ 
    //example data class, not used 
}; 

class MyBuffer : public std::basic_streambuf<char, std::char_traits<char> > 
{ 

public: 

    inline MyBuffer(MyData data) : 
    data(data) 
    { 
     setp(buf, buf + BUF_SIZE); 
    } 

protected: 

    // This is called when buffer becomes full. If 
    // buffer is not used, then this is called every 
    // time when characters are put to stream. 
    inline virtual int overflow(int c = Traits::eof()) 
    { 
#ifdef DEBUG 
     std::cout << "(over)"; 
#endif 
     // Handle output 
     putChars(pbase(), pptr()); 
     if (c != Traits::eof()) { 
      char c2 = c; 
      // Handle the one character that didn't fit to buffer 
      putChars(&c2, &c2 + 1); 
     } 
     // This tells that buffer is empty again 
     setp(buf, buf + BUF_SIZE); 

     return c; 
    } 

    // This function is called when stream is flushed, 
    // for example when std::endl is put to stream. 
    inline virtual int sync(void) 
    { 
     // Handle output 
     putChars(pbase(), pptr()); 
     // This tells that buffer is empty again 
     setp(buf, buf + BUF_SIZE); 
     return 0; 
    } 

private: 

    // For EOF detection 
    typedef std::char_traits<char> Traits; 

    // Work in buffer mode. It is also possible to work without buffer. 
    static const size_t BUF_SIZE = 64; 
    char buf[BUF_SIZE]; 

    // This is the example userdata 
    MyData data; 

    // In this function, the characters are parsed. 
    inline void putChars(const char* begin, const char* end){ 
#ifdef DEBUG 
     std::cout << "(putChars(" << static_cast<const void*>(begin) << 
      "," << static_cast<const void*>(end) << "))"; 
#endif 
     //just print to stdout for now 
     for (const char* c = begin; c < end; c++){ 
      std::cout << *c; 
     } 
    } 

}; 

class MyOStream : public std::basic_ostream< char, std::char_traits<char> > 
{ 

public: 

    inline MyOStream(MyData data) : 
    std::basic_ostream< char, std::char_traits<char> >(&buf), 
    buf(data) 
    { 
    } 

private: 

    MyBuffer buf; 

}; 

int main(void) 
{ 
    MyData data; 
    MyOStream o(data); 

    for (int i = 0; i < 8; i++) 
     o << "hello world! "; 

    o << std::endl; 

    return 0; 
} 
19

Otro trabajo cortar para lograr un efecto similar es el uso de la plantilla y la composición

class LoggedStream { 
public: 
    LoggedStream(ostream& _out):out(_out){} 
    template<typename T> 
    const LoggedStream& operator<<(const T& v) const {log();out << v;return *this;} 
protected: 
    virtual void log() = 0; 
    ostream& out; 
}; 

class Logger : LoggedStream { 
    void log() { std::cerr << "Printing" << std::endl;} 
}; 

int main(int,char**) {LoggedStream(std::cout) << "log" << "Three" << "times";} 
+0

esto no admite cosas como 'hex' o' endl' – shoosh

+0

¿Por qué no? 'T' obtendrá el tipo' std :: hex'. –

+6

@ElazarLeibovich: Funciona para 'std :: hex' pero no para' std :: endl' o 'std :: flush' (y probablemente algunos otros). Esto se debe a que no puede resolver T al tipo de función apropiado. Agregar lo siguiente a 'LoggedStream' resuelve el problema:' LogstStream const & operator << (std :: ostream & (* F) (std :: ostream &)) const {F (out); devuelve * esto; } ' –

12

me daba vueltas en mi cabeza cómo hacer lo mismo y descubrí que es en realidad no es tan difícil. Básicamente solo subclase los objetos ostream y streambuf, y construye el ostream consigo mismo como el búfer. se utilizará el desbordamiento virtual() de std :: streambuf para cada carácter enviado a la transmisión. Para adaptarse a su ejemplo, acabo de hacer una función foo() y la llamé.

struct Bar : std::ostream, std::streambuf 
{ 
    Bar() : std::ostream(this) {} 

    int overflow(int c) 
    { 
     foo(c); 
     return 0; 
    } 


    void foo(char c) 
    { 
     std::cout.put(c); 

    } 
}; 

void main() 
{ 
    Bar b; 
    b<<"Look a number: "<<std::hex<<29<<std::endl; 
} 

oh e ignore el hecho de que la función principal no es una función principal real. Está en un espacio de nombres llamado desde otro lugar; p

+2

¡Funciona a la perfección! Debería ser la respuesta aceptada, aunque es una vieja pregunta. – ZXcvbnM

+0

Estoy de acuerdo con ZXcvbnM, esta debería ser la respuesta aceptada. La respuesta aceptada contiene una referencia útil, pero realmente no proporciona una solución. Ben proporciona una solución de trabajo simple. +1. – sgbirch

+0

Finalmente encontré una solución después de buscarla durante tanto tiempo, esta debería ser la respuesta aceptada, como decía la gente. – user0103

Cuestiones relacionadas