Listing 1 tstream.h — template class definitions

#if !defined TSTREAM_H
#define TSTREAM_H

#include <iostream.h>  // for base class definitions

// class tbuf<>. Derived publicly from streambuf to
// allow class ios which contains pointer to streambuf
// access to virtual functions. tbuf<> implements
// basic buffering, reading, writing and seeking on
// a stream. It also provides open, close, attach,
// and utility functions.
template <class T>
class tbuf : public streambuf {
public:
// openProtection provides a default parameter to the
// open functions to specify what protection a file
// will be created with. You can ignore this if it is
// not necessary
static const int openProtect;
// tbufSize specifies the default buffer size. It is
// set to 516 bytes.
static const int tbufSize;
// Default contructor. Make a buffer without a
// stream attached. mode has a dual meaning, if it
// is zero it means that any operation is allowable,
// and the stream should not be deleted when closing.
   tbuf()
      : stream(0), mode(0), opened(0)
   {
      makbuf();
   }
// create buffer and attach to t.  t is assumed to be
// already opened in read/write mode.  t will not be
// deleted or closed when closing this buffer
   tbuf(T &t)
      : stream(&t), mode(0), opened(1)
   {
      makbuf();
   }
// create buffer from parameters, and attach to t.
   tbuf(T &t, char* b, int l)
      : stream(&t), mode(0), opened(1)
   {
      setbuf(b, l);
   }
// destroy buffer. If mode is not zero, t will be
// closed and deleted. Otherwise just flush the
// output buffer.
   ~tbuf()
   {
      if(mode)
         close();
      else
         overflow(EOF);
   }
// return open status
   int is_open() const
   {
      return opened;
   }
// return reference to stream
   T &fd() const
   {
      return *stream;
   }
// open stream. mode must not be zero.  stream will
// be closed and deleted when closing buffer.
   tbuf *open(const char *name, int mode,
                   int prot = tbuf::openProtect);
// close buffer and optionally delete stream.
   tbuf *close();
// attach stream to buffer.  Stream is assumed to
// be opened in read/write mode.
   tbuf *attach(T &);
// write buffer to stream and reset pointers.
   virtual int overflow(int = EOF);
// read data into buffer and reset pointers.
   virtual int underflow();
// sync input and output.
   virtual int sync();
// seek to offset and flush output buffers.
   virtual long seekoff(long, ios::seek_dir, int);
// set buffer.  For unbuffered i/o set char * to 0.
   virtual streambuf *setbuf(char *, int);
protected:
   int putBackSize()
   {
      return (blen() > 8) ? 4 : 1;
   }
   void resetpg(int end = 0);
   void makbuf()
   {
      setbuf(new char[tbufSize], tbufSize);
   }
   int unbuffered()
   {
      return streambuf::unbuffered() | !base();
   }
   T     *stream;
   int   mode;
   short opened;
   char  lookAhead[2];
};

template <class T>
const int tbuf<T>::tbufSize = 516;
template <class T>
const int tbuf<T>::openProtect = 0;

// Attach an open stream to this buffer. When this
// buffer is closed don't try to close stream. If
// not yet buffered, try to create a buffer. Reset
// the put and get pointers. Return 0 on error, or
// this if OK.
template <class T>
tbuf<T>* tbuf<T>::attach(T &t)
{
   if(opened)      // if already opened, return 0
      return 0;
   stream = &t;    // attach stream.
   opened = 1;     // set to opened.
   mode = 0;       // buffer doesn't own stream.
   if(!base())     // if no buffer yet...
      makbuf();   // try to make one.
   else
      resetpg(); // reset put and get pointers.
   return this;
}

// Close buffer, and optionally stream attached to it.
// Flush buffer if data inside. Return 0 on error or
// this if OK.
template <class T>
tbuf<T>* tbuf<T>::close()
{
   if(!*stream)    // if stream closed,
      opened = 0; // set opened off.
   if(!opened)     // if not on return 0.
      return 0;
   int ret = 0;
   if(out_waiting()) // if output in buffer.
          // flush output buffer.
      ret = (overflow(EOF) == EOF) ? 1 : 0;
   if(mode)        // if mode is 0, don't
      delete stream;  // close or delete stream.
   opened = 0;     // set opened off.
   return ret ? 0 : this;
}
// Write data from buffer to stream. This function
// is called when the buffer is full and we need to
// output more characters. The parameter c is the
// character that caused the overflow. If the
// stream is buffered, it will be placed in the
// buffer, otherwise it is written to the stream.
// If input char is EOF, don't write, just flush.
// Returns EOF on error, or 1 on success.
template <class T>
int tbuf<T>::overflow(int c)
{
   if(!opened ||    // check to see if stream
      mode == 0 ||     // is on and mode is out.
      !(mode&ios::out))
      return EOF;
   if(unbuffered())
   {
      if(c ! = EOF)
      {        // if unbuffered,
         char b = c; // write single char
         if(stream->write(&b, 1) != 1)
            return EOF;
      }
   }
   else       // else if buffered.
   {
      if(sync() != 0) // sync input and output
         return EOF; // when writing
      resetpg(1); // reset the put/get pointers
      if(c != EOF)
      {
         sputc(c);   // add c to the buffer
         gbump(1);   // move the get pointer
      }
   }
   return 1;       // return OK
}

// Open stream. If mode ios::ate (at end) is on,
// seek to end
template <class T>
tbuf<T>* tbuf<T>::open(const char* n, int m, int p)
{
   if(opened || !m)    //if already on, or no mode,
      return 0;   // return error.
   stream = new T; // make new stream pointer.
   stream->open(n, m, p);// open stream.
   if(!*stream)     // if stream not open,
      return 0;    // return error.
   opened = 1;      // set to on.
   mode = m;        // remeber mode.
   if((mode & ios::ate) &&
         stream->seek(OL, ios::end) == EOF)
      return 0;    // seek to end if ios::ate.
   resetpg();       // reset put/get pointers.
   return this;     // return OK.
}

// Set the buffer, reset the put/get pointers.
// Return 0 on error, this if OK.
template <class T>
streambuf* tbuf<T>::setbuf(char* b, int l)
{
   if(!b)      // turn off buffering.
   {
      streambuf::unbuffered(1);
      return this;
   }
   if(opened && base())// check if stream is opened,
      return 0;       // , and no buffer yet.
   setb(b, b+l, 0);    // set buffer pointers.
   resetpg();      // reset put/get pointers.
   return this;    // return OK.
}

// Seek to offset. dir indicates the relativity of
// the offset, either from beginning, current position,
// or end (ios::beg, ios::cur, ios::end).
// First make sure there's nothing in the buffer.
template <class T>
long tbuf<T>::seekoff(long off, ios::seek_dir dir,
                        int /* mode ignored */)
{
   long loff = off;
   int count = out_waiting();  // flush output first.
   if(count)
   {
      if(stream->write(pbase(), count) != count)
         return EOF;
   }
   else if(dir == ios::cur && // if relative seek,
         (count = in_avail()) != 0)
      loff -= count;      // discount input.
   resetpg();          // reset pointers.
   return stream->seek(loff, dir);
}
// sync input and output buffer pointers. If output
// is waiting, write it, if input is waiting,
// back up to read it again
template <class T>
int tbuf<T>::sync()
{
   if (!opened)        // check if opened.
      return EOF;
   int count = out_waiting();  // check for output,
   if(count)           // in buffer.
   {
      if(stream->write(pbase(), count) != count)
         return EOF;       // write output.
      resetpg(1);
   }
   else if(in_avail())      // check for input
   {               // in buffer
      long pos = stream->seek(long(-in_avail()),
                                   ios::cur);
      setg(eback(), gptr(), gptr());
      setp(gptr(), gptr());   // set up to re-read.
      if(pos == EOF)
         return EOF;
   }
   return 0;
}

// Read from stream to fill buffer. Must be opened and
// in input mode. If there is input in the buffer,
// return it. Otherwise call sync, read from stream,
// and set pointers. If the input is unbuffered,
// use the lookahead buffer.
template <class T>
int tbuf<T>::underflow()
{
   if(!opened || mode == 0 || !(mode&ios::in))
      return EOF; // check for correct mode.
   if(in_avail())      // if available from buffer,
      return *gptr()&0xFF;    // don't bother.
   int c;
   int count;
   if(unbuffered())        // if unbuffered.
   {
      count = stream->read(lookAhead, 1);
      if(count == EOF)     // read one char
      {           // into look ahead
         c = EOF;         // buffer.
         setg(0, 0, 0);
      }
      else
      {
         c = lookAhead[0]&0xFF;
         setg(lookAhead, lookAhead, lookAhead+1);
      }
   }
   else           // else buffered.
   {
      if(sync() != 0)    // sync pointers.
         return EOF;
      int pb = putBackSize();
      char *b = base();   // read into buffer.
      count = stream->read(b+pb, blen()-pb);
      if(count == EOF)    // check read return.
         return EOF;
      setg(b, b+pb, b+pb+count);
      setp(b+pb, b+pb);   // set pointers.
      if(count)        // return first char.
         c = *gptr()&0xFF;
   }
   if(!count)
      c = EOF;
   return c;
}

// reset the put and get pointers. If end is
// true set the end pointers to the end of
// the buffer.
template<class T>
void tbuf<T>::resetpg(int end)
{
   char *buf = base(); // get the start of buffer.
   char *sbuf = buf + (buf ? putBackSize() : 0);
   char *ebuf;     // set start and end pointers.
   if(end)
      ebuf = buf + blen();
   else
      ebuf = sbuf;
   setp(sbuf, ebuf);   // set put pointer
   setg(buf, sbuf, sbuf);// set get pointer
}

// tstreambase is virtually derived from ios. ios
// does not define any functionality for tstreambase
// but allows communication between tstreambase and
// istream, ostream and stream classes. The functions
// defined in tstreambase typically call the same
// named function for its tbuf<> member and set ios
// status flags. When a tstreambase is constructed
// ios is initialized with a pointer to the tbuf<>
// member. ios see this pointer as a streambuf
// pointer, and only has access to tbuf<> through the
// virtual functions, overflow, underflow, sync, and
// seekoff.
template <class T>
class tstreambase : virtual public ios {
public:
// Default constructor. Make an unattached
// buffer, and initialize ios with it's pointer.
   tstreambase()
      : tbbuf()
   {
      ios::init(&tbbuf);
   }
// Make a buffer attach to named stream, and open
// stream
   tstreambase(const char* n, int m, int p = tbuf<T>::openProtect)
      : tbbuf()
   {
      ios::init(&tbbuf);
      open(n, m, p);
   }
// Make a buffer attached to already opened stream.
   tstreambase(T &f)
      : tbbuf(f)
   {
      ios::init(&tbbuf);
   }
// Make a buffer using parameters, and attach
// to already opened stream.
   tstreambase(T &f, char* b, int l)
      : tbbuf(f, b, l)
   {
      ios::init(&tbbuf);
   }
// Destroy buffer
   ~tstreambase()
   {
      ;
   }
// close attached stream and set ios flags.
   void close()
   {
      if(tbbuf.close())
         clear(ios::goodbit);
      else
         setstate(ios::failbit);
   }
// set buffer and set ios flags.
   void tstreambase::setbuf(char* b, int l)
   {
      if(tbbuf.setbuf(b, l))
         clear(ios::goodbit);
      else
         setstate(ios::failbit);
   }
// open stream and set ios flags.
   void open(const char *, int,
                int = tbuf<T>::openProtect);
// attach opened stream and set ios flags.
   void attach(T &);
// return the buffer.
   tbuf<T> *rdbuf()
   {
      return &tbbuf;
   }
private:
   tbuf<T> tbbuf;
};

// Attempt to open tbuf<>, and set ios flags
// accordingly. Opening an already open stream
// results in an error. If ios::app (append to
// file) is set, also set ios::out. If ios::out
// is set, and ios::app, ios::in, and ios::ate
// (start at end) are not set, set the ios::trunc bit.
template <class T>
void tstreambase<T>::open(const char *n, int m, int p)
{
   if(m & ios::app)
      m |= ios::out;
   else if((m & (ios::out|ios::ate|ios::app|ios::in))
                                   == ios::out)
      m |= ios::trunc;
   if(tbbuf.is_open())
      clear(ios::failbit);
   else if(tbbuf.open(n, m, p))
      clear(ios::goodbit);
   else
      clear(ios::badbit);
}

// Attach an opened stream to buffer, and set the ios
// bits accordingly.
template <class T>
void tstreambase<T>::attach(T &f)
{
   if(tbbuf.is_open())
      setstate(ios::failbit);
   else if(tbbuf.attach(f))
      clear(ios::goodbit);
   else
      clear(ios::badbit);
}

// The itstream, otstream and tstream class are merely
// "shell" classes, and serve only to combine the
// functionality of it's base class. The only functions
// defined are the constructor and destructors, and
// open and rdbuf. There are no addition data members
// and all functions call directly the functions of the
// base class. The default open mode is ios::in for
// itstream, ios::out for otstream, and both for
// tstream.
template <class T>
class itstream : public tstreambase<T>,
                        public istream {
public:
   itstream()
   {
      ;
   }
   itstream(const char* n, int m = ios::in,
                   int p = tbuf<T>::openProtect)
      : tstreambase<T>(n, m | ios::in, p)
   {
      ;
   }
   itstream(T &f)
      : tstreambase<T>(f)
   {
      ;
   }
   itstream(T &f, char* b, int l)
      : tstreambase<T>(f, b, l)
   {
      ;
   }
   ~itstream()
   {
      ;
   }
   tbuf<T> *rdbuf()
   {
      return tstreambase<T>::rdbuf();
   }
   void open(const char *n, int m = ios::in,
                   int p = tbuf<T>::openProtect)
   {
      tstreambase<T>::open(n, m | ios::in, p);
   }
};
template <class T>
class otstream : public tstreambase<T>,
                          public ostream {
public:
   otstream()
   {
      ;
   }
   otstream(const char* n, int m = ios::out,
                int p = tbuf<T>::openProtect)
      : tstreambase<T>(n, m | ios::out, p)
   {
      ;
   }
   otstream(T &f)
      : tstreambase<T>(f)
   {
      ;
   }
   otstream(T &f, char* b, int l)
      : tstreambase<T>(f, b, l)
   {
      ;
   }
   ~otstream()
   {
      ;
   }
   tbuf<T> *rdbuf()
   {
      return tstreambase<T>::rdbuf();
   }
   void open(const char *n, int m = ios::out,
                 int p = tbuf<T>::openProtect)
   {
      tstreambase<T>::open(n, m | ios::out, p);
   }
};

template <class T>
class tstream : public tstreambase<T>,
                       public iostream {
public:
   tstream()
   {
      ;
   }
   tstream(const char *n, int m, int p = tbuf<T>::openProtect)
      : tstreambase<T>(n, m, p)
   {
      ;
   }
   tstream(T &f)
      : tstreambase<T>(f)
   {
      ;
   }
   tstream(T &f, char *b, int l)
      : tstreambase<T>(f, b, l), iostream()
   {
      ;
   }
   ~tstream()
   {
      ;
   }
   tbuf<T> *rdbuf()
   {
      return tstreambase<T>::rdbuf();
   }
   void open(const char *n, int m,
            int p = tbuf<T>::openProtect)
   {
      tstreambase<T>::open(n, m, p);
   }
};
#endif
/* End of File */