2009-08-05 6 views
9

J'ai besoin de faire le pont entre deux bibliothèques sur un flux.Héritage de std :: istream ou équivalent

QDataStream which is a stream from Qt 

et une fonction d'une autre bibliothèque qui ressemble à ceci

void read_something(istream& i); 

Je ne contrôle pas sur la façon dont le QDataStream est créé et je ne suis pas autorisé à changer l'interface de read_somthing fonction.

La première chose à laquelle je peux penser est d'écrire une classe qui hérite de istream et encapsule QDataStream. Est-ce que quelqu'un a déjà fait ça?

Si ce que je pensais n'était pas la bonne façon, je me demande quelle est la meilleure façon d'y parvenir.

+0

Êtes-vous écrit votre propre implémentation de read_something, ou essayez-vous d'appeler cette fonction? – Ropez

Répondre

14

Ce que vous devez faire est d'écrire un streambuf qui utilise QDataStream readBytes et writeBytes pour implémenter ses fonctions. Puis enregistrez le streambuf dans un istream avec rdbuf (vous pouvez également écrire un descendant istream qui le fait lors de l'initialisation). Boost contient une bibliothèque visant à faciliter l'écriture de streambuf. Il pourrait être plus simple de l'utiliser que de comprendre l'interface streambuf (personnellement je ne l'ai jamais utilisé mais j'ai écrit plusieurs streambuf, je verrai si j'ai un exemple que je peux poster).

Editer: voici quelque chose (il vient du français FAQ de fr.comp.lang.C++ -, je n'ai pas le temps de traduire et je pense qu'il est préférable de les quitter que de les supprimer) qui enveloppe FILE * call dans un streambuf. C'est aussi un cas d'utilisation d'un héritage privé: s'assurer que ce qui pourrait être un membre est initialisé avant une classe de base. Dans le cas de IOStream, la classe de base pourrait aussi bien recevoir un pointeur NULL et ensuite le membre init() utilisé pour définir le 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

Notez qu'une alternative beaucoup plus facile serait d'utiliser les concepts de puits et de sources. Par exemple, un objet récepteur encapsulera le FICHIER * et n'aura besoin que d'implémenter une fonction d'écriture dans laquelle un appel à fwrite sera effectué sur le pointeur FILE. Le récepteur peut alors être enveloppé dans un boost :: iostream :: stream qui peut ensuite être traité comme un std :: ostream. Ah je vois quelqu'un a déjà indiqué cette solution ci-dessous. –

8

La solution de flux de boost:

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

cela fonctionne comme un charme – lyxera

+0

merci beaucoup! – lyxera

+0

@lyxera J'ai aussi fait un pont de puits - Source - Device QIODevice: stackoverflow.com/questions/848269/mixing-qt-with-stl-and-boost-are-there-any-bridges-to-make-it -easy/856812 # 856812 – TimW