I did this awhile ago to help someone out, but I make no claim as to its robustness. It was more of an example than anything. But, here you go:
Code:
#ifndef H_FDIO
#define H_FDIO 1
#include <algorithm> // std::min
#include <cerrno> // errno, EINTR
#include <cstdio> // BUFSIZ, EOF
#include <cstring> // std::memcpy
#include <istream> // std::streambuf, std::[i,o,io]stream
extern "C" {
#include <unistd.h> // read, write
}
namespace fdio {
// derived streambuf class, full support for buffered reading and writing
class streambuf : public std::streambuf {
protected:
int fd; // file descriptor
enum { BACKSIZE = 8 }; // putback area size
enum { PUTSIZE = BUFSIZ }; // put area size
enum { GETSIZE = BUFSIZ }; // get area size
char pbuf[PUTSIZE]; // put area
char gbuf[GETSIZE + BACKSIZE]; // get and putback areas
char *const gbase; // get area base pointer
// -- flush: output to destination.
// returns number of characters sent,
// or EOF upon failure
int flush() {
// calculate # characters to send
std::ptrdiff_t n = pptr() - pbase();
// send characters to destination
if ( write(fd, pbase(), n) != n )
return EOF;
// reset put pointer
pbump(-n);
return n;
}
// -- overridden virtual functions
virtual int sync() {
return flush() == EOF ? -1 : 0;
}
virtual int overflow(int c) {
if ( c != EOF ) {
*pptr() = c; // use last free space (see ctor)
pbump(1);
}
return flush() == EOF ? EOF : c;
}
virtual int underflow() {
if ( gptr() < egptr() ) // characters available in get area?
return *gptr();
// set putback area
std::ptrdiff_t nback = std::min<std::ptrdiff_t>(gptr() - eback(), BACKSIZE);
std::memcpy(gbase - nback, gptr() - nback, nback);
// read new characters
ssize_t nread = read(fd, gbase, GETSIZE);
if ( nread <= 0 )
return EOF;
// reset get area pointers
setg(gbase - nback, gbase, gbase + nread);
return *gptr();
}
public:
// -- ctor
streambuf(int file_descriptor)
: fd (file_descriptor)
, gbase (gbuf + BACKSIZE)
{
setg(gbase, gbase, gbase); // trigger underflow call on first read
setp(pbuf, pbuf+PUTSIZE-1); // one space left for overflow
}
// -- xtor
virtual ~streambuf() {
sync();
}
};
// derived ostream class
class ostream : public std::ostream {
public:
ostream(int fd)
: std::ostream (0)
, sbuf (fd)
{ init(&sbuf); }
protected:
fdio::streambuf sbuf;
};
// derived istream class
class istream : public std::istream {
public:
istream(int fd)
: std::istream (0)
, sbuf (fd)
{ init(&sbuf); }
protected:
fdio::streambuf sbuf;
};
// derived iostream class
class iostream : public std::iostream {
public:
iostream(int fd)
: std::iostream (0)
, sbuf (fd)
{ init(&sbuf); }
protected:
fdio::streambuf sbuf;
};
}
#endif // H_FDIO
And a cheap example program to exercise and show it's capabilities:
Code:
#include "fdio.h"
void hello(fdio::streambuf&);
void world(fdio::streambuf&);
int main() {
fdio::streambuf out(1);
hello(out);
world(out);
out.pubsync();
}
void hello(fdio::streambuf& out) {
out.sputc('H');
out.sputc('e');
out.sputn("llo ", 4);
}
void world(fdio::streambuf& out) {
out.sputc('W');
out.sputn("orld!", 5);
out.sputc('\n');
}
Like I said....no warranties here. I just pulled it out of some old stuff. You can use the [i,o,io]stream classes if you want formatted I/O as well. Good Luck!
I'd recommend Josuttis' book on the C++ STL for more about using streambuf and friends. I have another book I actually like better, called Standard C++ IOStreams, but that's alot to chew at first.