2009-08-05 11 views
9

Necesito unir dos bibliotecas en una secuencia.Heredar std :: istream o equivalente

QDataStream which is a stream from Qt 

y alguna función de otra bibliotecas que se parece a esto

void read_something(istream& i); 

tengo ningún control sobre cómo se crea la QDataStream y no se me permite cambiar la interfaz de la función read_somthing.

Lo primero que se me ocurre es escribir una clase que hereda istream y envuelve QDataStream. ¿Alguien ha hecho eso antes?

Si lo que pensé que no era la forma correcta, me pregunto cuál es la mejor manera de lograrlo.

+0

se desea escribir su propia implementación de read_something, o está tratando de llamar a esta función? – Ropez

Respuesta

14

Lo que debe hacer es escribir un streambuf que use QDataStream readBytes y writeBytes para implementar sus funciones. A continuación, registre el streambuf en un istream con rdbuf (también puede escribir un descendiente istream que lo hace cuando se inicializa).

Boost contiene una biblioteca con el objetivo de facilitar la escritura de streambuf. Podría ser más sencillo usarlo que entender la interfaz de streambuf (personalmente, nunca lo he usado, pero he escrito varios streambuf; veré si tengo un ejemplo que puedo publicar).

Editar: aquí hay algo (comentado en francés - proviene de las preguntas frecuentes en francés de fr.comp.lang.C++ -, no tengo tiempo para la traducción y creo que es mejor dejarlos que eliminarlos)) que envuelve la llamada FILE * en un streambuf. Este también es un ejemplo de uso de herencia privada: garantizar que lo que podría ser miembro se inicialice antes de una clase base. En el caso de IOStream, la clase base también podría recibir un puntero NULL y luego el miembro init() utilizado para establecer el streambuf.

#include <stdio.h> 
#include <assert.h> 

#include <iostream> 
#include <streambuf> 

// streambuf minimal encapsulant un FILE* 
// - utilise les tampons de FILE donc n'a pas de tampon interne en 
//  sortie et a un tampon interne de taille 1 en entree car l'interface 
//  de streambuf ne permet pas de faire moins; 
// - ne permet pas la mise en place d'un tampon 
// - une version plus complete devrait permettre d'acceder aux 
//  informations d'erreur plus precises de FILE* et interfacer aussi 
//  les autres possibilites de FILE* (entre autres synchroniser les 
//  sungetc/sputbackc avec la possibilite correspondante de FILE*) 

class FILEbuf: public std::streambuf 
{ 
public: 

    explicit FILEbuf(FILE* cstream); 
    // cstream doit etre non NULL. 

protected: 

    std::streambuf* setbuf(char_type* s, std::streamsize n); 

    int_type overflow(int_type c); 
    int  sync(); 

    int_type underflow(); 

private: 

    FILE* cstream_; 
    char  inputBuffer_[1]; 
}; 

FILEbuf::FILEbuf(FILE* cstream) 
    : cstream_(cstream) 
{ 
    // le constructeur de streambuf equivaut a 
    // setp(NULL, NULL); 
    // setg(NULL, NULL, NULL); 
    assert(cstream != NULL); 
} 

std::streambuf* FILEbuf::setbuf(char_type* s, std::streamsize n) 
{ 
    // ne fait rien, ce qui est autorise. Une version plus complete 
    // devrait vraissemblablement utiliser setvbuf 
    return NULL; 
} 

FILEbuf::int_type FILEbuf::overflow(int_type c) 
{ 
    if (traits_type::eq_int_type(c, traits_type::eof())) { 
    // la norme ne le demande pas exactement, mais si on nous passe eof 
    // la coutume est de faire la meme chose que sync() 
    return (sync() == 0 
     ? traits_type::not_eof(c) 
     : traits_type::eof()); 
    } else { 
    return ((fputc(c, cstream_) != EOF) 
     ? traits_type::not_eof(c) 
     : traits_type::eof()); 
    } 
} 

int FILEbuf::sync() 
{ 
    return (fflush(cstream_) == 0 
     ? 0 
     : -1); 
} 

FILEbuf::int_type FILEbuf::underflow() 
{ 
    // Assurance contre des implementations pas strictement conformes a la 
    // norme qui guaranti que le test est vrai. Cette guarantie n'existait 
    // pas dans les IOStream classiques. 
    if (gptr() == NULL || gptr() >= egptr()) { 
    int gotted = fgetc(cstream_); 
    if (gotted == EOF) { 
     return traits_type::eof(); 
    } else { 
     *inputBuffer_ = gotted; 
     setg(inputBuffer_, inputBuffer_, inputBuffer_+1); 
     return traits_type::to_int_type(*inputBuffer_); 
    } 
    } else { 
    return traits_type::to_int_type(*inputBuffer_); 
    } 
} 

// ostream minimal facilitant l'utilisation d'un FILEbuf 
// herite de maniere privee de FILEbuf, ce qui permet de s'assurer 
// qu'il est bien initialise avant std::ostream 

class oFILEstream: private FILEbuf, public std::ostream 
{ 
public: 
    explicit oFILEstream(FILE* cstream); 
}; 

oFILEstream::oFILEstream(FILE* cstream) 
    : FILEbuf(cstream), std::ostream(this) 
{ 
} 

// istream minimal facilitant l'utilisation d'un FILEbuf 
// herite de maniere privee de FILEbuf, ce qui permet de s'assurer 
// qu'il est bien initialise avant std::istream 

class iFILEstream: private FILEbuf, public std::istream 
{ 
public: 
    explicit iFILEstream(FILE* cstream); 
}; 

iFILEstream::iFILEstream(FILE* cstream) 
    : FILEbuf(cstream), std::istream(this) 
{ 
} 

// petit programme de test 
#include <assert.h> 
int main(int argc, char* argv[]) 
{ 
    FILE* ocstream = fopen("result", "w"); 
    assert (ocstream != NULL); 
    oFILEstream ocppstream(ocstream); 
    ocppstream << "Du texte"; 
    fprintf(ocstream, " melange"); 
    fclose(ocstream); 
    FILE* icstream = fopen("result", "r"); 
    assert (icstream != NULL); 
    iFILEstream icppstream(icstream); 
    std::string word1; 
    std::string word2; 
    icppstream >> word1; 
    icppstream >> word2; 
    char buf[1024]; 
    fgets(buf, 1024, icstream); 
    std::cout << "Got :" << word1 << ':' << word2 << ':' << buf << '\n'; 
} 
+0

Tenga en cuenta que una alternativa mucho más fácil sería utilizar conceptos de receptor y fuente. Por ejemplo, un objeto sumidero envolvería el ARCHIVO * y solo necesitaría implementar una función de escritura dentro de la cual se realiza una llamada para escribir sobre el puntero ARCHIVO. El fregadero puede envolverse en un impulso :: iostream :: stream que luego se puede tratar como un std :: ostream. Ah, veo que alguien ya ha indicado esta solución a continuación. –

8

La solución de flujo de impulso:

namespace boost { 
    namespace iostreams { 

     class DataStreamSource 
     { 
     public: 
      typedef char char_type; 
      typedef source_tag category; 

      DataStreamSource(QDataStream *const source) : m_source(source){ 
      } 
      std::streamsize read(char* buffer, std::streamsize n) { 
       return m_source ? m_source->readRawData(buffer, n) : -1; 
      } 

     private: 
      QDataStream *const m_source; 
     }; 
    } 
} 

// using DataStreamSource 
namespace io = boost::iostreams; 
QFile fl("temp.bin"); 
fl.open(QIODevice::ReadOnly); 
QDataStream s(&fl); 
io::stream<io::DataStreamSource> dataStream(&s); 
read_something(dataStream); 
+0

esto funciona como un encanto – lyxera

+0

muchas gracias! – lyxera

+0

@lyxera También hice un puente Sink - Source - Device QIODevice: stackoverflow.com/questions/848269/mixing-qt-with-stl-and-boost-are-herehere-any-bridges-to-make-it -easy/856812 # 856812 – TimW

Cuestiones relacionadas