2009-08-05 12 views
9

Potrzebuję połączyć dwie biblioteki nad strumieniem.Dziedziczenie std :: istream lub równoważnego

QDataStream which is a stream from Qt 

a niektóre funkcje z innych bibliotek, które wygląda tak

void read_something(istream& i); 

nie mam kontrolę nad tym, w jaki sposób QDataStream jest tworzony i nie wolno mi zmienić interfejs funkcji read_somthing.

Pierwszą rzeczą, o której mogę myśleć jest napisanie klasy, która dziedziczy istream i owija QDataStream. Czy ktoś wcześniej to zrobił?

Jeśli to, co uważałem za niewłaściwe, zastanawiam się, jaki jest najlepszy sposób, aby to osiągnąć.

+0

Piszesz swoją realizację read_something, czy starasz się wywołać tę funkcję? – Ropez

Odpowiedz

14

Należy napisać streambuf, który wykorzystuje readBytes QDataStream i writeBytes do implementacji swoich funkcji. Następnie zarejestruj streambuf w istream za pomocą rdbuf (możesz także napisać potomka istream, który robi to po zainicjowaniu).

Boost zawiera bibliotekę mającą na celu ułatwienie pisania streambuf. Może być prostsze w użyciu niż zrozumienie interfejsu streambuf (osobiście nigdy go nie używałem, ale napisałem wiele streambuf, zobaczę, czy mam przykład, który mogę opublikować).

Edytuj: tutaj jest coś (skomentował po francusku - pochodzi z francuskiego FAQ fr.comp.lang.C++ -, nie mam czasu na tłumaczenie i myślę, że lepiej jest je zostawić niż je usunąć), który opakowuje FILE * wywołanie w streambuf. Jest to również przykład użycia prywatnego dziedziczenia: zapewnienie, że to, co może być członkiem, zostanie zainicjowane przed klasą podstawową. W przypadku IOStream klasa podstawowa mogłaby również otrzymać wskaźnik NULL, a następnie element init() użyty do ustawienia 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

Zauważ, że znacznie łatwiejszą alternatywą byłoby użycie koncepcji pochłaniania i źródła. Na przykład, obiekt sink będzie owijał PLIK * i będzie musiał tylko zaimplementować funkcję zapisu, w której wywoływane jest wywołanie fwrite nad wskaźnikiem FILE. Zlew można następnie owijać w: :: iostream :: stream , który można następnie traktować jak std :: ostream. Ach, widzę, że ktoś już wskazał to rozwiązanie poniżej. –

8

Wzmocnienie rozwiązanie stream:

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

to działa jak wdzięk – lyxera

+0

dziękuję bardzo! – lyxera

+0

@lyxera Zrobiłem też Zlew - Źródło - Urządzenie Most QIODevice: stackoverflow.com/questions/848269/mixing-qt-with-stl-and-boost-are-there-any-bridges-to-make-it -asy/856812 # 856812 – TimW

Powiązane problemy