Наследование std :: istream или эквивалентного - PullRequest
12 голосов
/ 05 августа 2009

Мне нужно соединить две библиотеки через поток.

QDataStream which is a stream from Qt

и некоторые функции из других библиотек, которые выглядят так:

void read_something(istream& i);

Я не могу контролировать, как создается QDataStream, и мне не разрешено изменять интерфейс функции read_somthing.

Первое, о чем я могу подумать, это написать класс, который наследует istream и оборачивает QDataStream. Кто-нибудь делал это раньше?

Если то, что я думал, было неправильным способом, я задаюсь вопросом, каков наилучший способ достичь этого.

Ответы [ 2 ]

17 голосов
/ 05 августа 2009

Что вы должны сделать, это написать потоковый буфер, который использует QDataStream readBytes и writeBytes для реализации своих функций. Затем зарегистрируйте streambuf в istream с помощью rdbuf (вы также можете написать потомок istream, который делает это при инициализации).

Boost содержит библиотеку, предназначенную для облегчения написания streambuf. Использовать его может быть проще, чем понимать интерфейс streambuf (лично я никогда не использовал его, но я написал несколько streambuf; я посмотрю, есть ли у меня пример, который я могу опубликовать).

Редактировать: вот что-то (прокомментировано по-французски - взято из французского FAQ fr.comp.lang.c ++ - у меня нет времени на перевод и думаю, что лучше оставить их, чем удалить) который оборачивает вызов FILE * в streambuf. Это также показательный случай использования частного наследования: обеспечение инициализации того, что может быть членом, перед базовым классом. В случае IOStream базовый класс мог бы также получить указатель NULL, а затем член init (), используемый для установки streambuf. * ​​1005 *

#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';
}
8 голосов
/ 05 августа 2009

Решение для ускоренного потока:

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);
...