2009-08-05 41 views
9

Tôi cần phải nối hai thư viện qua một luồng.Kế thừa tiêu chuẩn :: istream hoặc tương đương

QDataStream which is a stream from Qt 

và một số chức năng từ các thư viện khác mà trông như thế này

void read_something(istream& i); 

tôi đã không kiểm soát như thế nào QDataStream được tạo ra và tôi không được phép thay đổi giao diện của chức năng read_somthing.

Điều đầu tiên tôi có thể nghĩ đến là viết một lớp kế thừa istream và kết thúc tốt đẹp QDataStream. Có ai làm điều đó trước đây không?

Nếu những gì tôi nghĩ không phải là cách thích hợp, tôi tự hỏi cách tốt nhất để đạt được điều đó là gì.

+0

Bạn đang viết thực hiện của riêng bạn read_something, hoặc bạn đang cố gắng để gọi chức năng này? – Ropez

Trả lời

14

Những gì bạn cần làm là viết một streambuf sử dụng các ReadBytes QDataStream và writeBytes để thực hiện các chức năng của nó. Sau đó đăng ký streambuf vào một istream với rdbuf (bạn cũng có thể viết một hậu duệ istream mà thực hiện điều này khi khởi tạo).

Boost chứa thư viện nhằm tạo điều kiện thuận lợi cho việc viết streambuf. Nó có thể đơn giản hơn để sử dụng nó hơn là hiểu giao diện streambuf (cá nhân tôi chưa bao giờ sử dụng nó nhưng tôi đã viết nhiều streambuf; tôi sẽ xem nếu tôi có một ví dụ mà tôi có thể đăng).

Chỉnh sửa: đây là một cái gì đó (nhận xét bằng tiếng Pháp - nó xuất phát từ câu hỏi thường gặp của fr.comp.lang.C++ -, tôi không có thời gian để dịch và nghĩ tốt hơn là bỏ chúng) kết thúc FILE * gọi vào một streambuf. Đây cũng là một trường hợp hiển thị của việc sử dụng thừa kế riêng: đảm bảo rằng những gì có thể là một thành viên được khởi tạo trước một lớp cơ sở. Trong trường hợp của IOStream, lớp cơ sở cũng có thể nhận được một con trỏ NULL và sau đó init thành viên() được sử dụng để thiết lập 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

Lưu ý một sự thay thế dễ dàng hơn nhiều là sử dụng các khái niệm chìm và nguồn. Ví dụ, một đối tượng sink sẽ quấn FILE * và sẽ chỉ cần thực hiện một hàm write trong đó một cuộc gọi được thực hiện để fwrite trên con trỏ FILE. Bồn rửa sau đó có thể được bọc trong một tăng :: iostream :: stream mà sau đó có thể được xử lý như một std :: ostream. Ah tôi thấy ai đó đã chỉ ra giải pháp này dưới đây. –

8

Việc tăng dòng giải pháp:

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

hoạt động như một nét duyên dáng – lyxera

+0

cảm ơn bạn rất nhiều! – lyxera

+0

@lyxera Tôi cũng đã thực hiện một thiết bị Sink - Source - Device QIODevice: stackoverflow.com/questions/848269/mixing-qt-with-stl-and-boost-are-there-any-bridges-to-make-it -easy/856812 # 856812 – TimW

Các vấn đề liên quan