Наследование std::istream или эквивалентного
Мне нужно соединить две библиотеки через поток.
QDataStream which is a stream from Qt
и некоторые функции из других библиотек, которые выглядят так
void read_something(istream& i);
Я не имею никакого контроля над тем, как создается QDataStream, и мне не разрешено изменять интерфейс функции read_somthing.
Первое, о чем я могу подумать, это написать класс, который наследует istream и оборачивает QDataStream. Кто-нибудь делал это раньше?
Если то, что я думал, было неправильным способом, я задаюсь вопросом, каков наилучший способ достичь этого.
2 ответа
Что вы должны сделать, это написать потоковый буфер, который использует QDataStream readBytes и writeBytes для реализации своих функций. Затем зарегистрируйте streambuf в istream с помощью rdbuf (вы также можете написать потомок istream, который делает это при инициализации).
Boost содержит библиотеку, предназначенную для облегчения написания streambuf. Это может быть проще, чем понимание интерфейса streambuf (лично я никогда не использовал его, но я написал несколько streambuf; я посмотрю, есть ли у меня пример, который я могу опубликовать).
Редактировать: вот что-то (прокомментировано по-французски - взято из французского FAQ fr.comp.lang.C++ - у меня нет времени на перевод, и я думаю, что лучше оставить их, чем удалить), что оборачивает FILE* позвонить в streambuf. Это также пример использования частного наследования: обеспечение инициализации того, что может быть членом, перед базовым классом. В случае IOStream базовый класс мог бы также получить указатель NULL, а затем член init(), используемый для установки 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';
}
Решение для ускоренного потока:
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);