The GNU C++ Iostream Library - 5. Using the streambuf Layer

Go to the first, previous, next, last section, table of contents.

# 5. Using the `streambuf` Layer

The `istream` and `ostream` classes are meant to handle conversion between objects in your program and their textual representation.

By contrast, the underlying `streambuf` class is for transferring raw bytes between your program, and input sources or output sinks. Different `streambuf` subclasses connect to different kinds of sources and sinks.

The GNU implementation of `streambuf` is still evolving; we describe only some of the highlights.

## 5.1 Areas of a `streambuf`

Streambuf buffer management is fairly sophisticated (this is a nice way to say "complicated"). The standard protocol has the following "areas":

- The put area contains characters waiting for output.
- The get area contains characters available for reading.

The GNU `streambuf` design extends this, but the details are still evolving.

The following methods are used to manipulate these areas. These are all protected methods, which are intended to be used by virtual function in classes derived from `streambuf`. They are also all ANSI/ISO-standard, and the ugly names are traditional. (Note that if a pointer points to the 'end' of an area, it means that it points to the character after the area.)

Method: char* streambuf::pbase () const
  Returns a pointer to the start of the put area.

Method: char* streambuf::epptr () const
  Returns a pointer to the end of the put area.

Method: char* streambuf::pptr () const
  If `pptr() < epptr ()`, the `pptr()` returns a pointer to the current put position. (In that case, the next write will overwrite `*pptr()`, and increment `pptr()`.) Otherwise, there is no put position available (and the next character written will cause `streambuf::overflow` to be called).

Method: void streambuf::pbump (int N)
  Add N to the current put pointer. No error checking is done.

Method: void streambuf::setp (char* P, char* E)
  Sets the start of the put area to P, the end of the put area to E, and the current put pointer to P (also).

Method: char* streambuf::eback () const
  Returns a pointer to the start of the get area.

Method: char* streambuf::egptr () const
  Returns a pointer to the end of the get area.

Method: char* streambuf::gptr () const
  If `gptr() < egptr ()`, then `gptr()` returns a pointer to the current get position. (In that case the next read will read `*gptr()`, and possibly increment `gptr()`.) Otherwise, there is no read position available (and the next read will cause `streambuf::underflow` to be called).

Method: void streambuf:gbump (int N)
  Add N to the current get pointer. No error checking is done.

Method: void streambuf::setg (char* B, char* P, char* E)
  Sets the start of the get area to B, the end of the get area to E, and the current put pointer to P.

## 5.2 Simple output re-direction by redefining `overflow`

Suppose you have a function `write_to_window` that writes characters to a `window` object. If you want to use the ostream function to write to it, here is one (portable) way to do it. This depends on the default buffering (if any).

    #include <iostream.h>
    /* Returns number of characters successfully written to win. */
    extern int write_to_window (window* win, char* text, int length);

    class windowbuf : public streambuf {
        window* win;
      public:
        windowbuf (window* w) { win = w; }
        int sync ();
        int overflow (int ch);
        // Defining xsputn is an optional optimization.
        // (streamsize was recently added to ANSI C++, not portable yet.)
        streamsize xsputn (char* text, streamsize n);
    };

    int windowbuf::sync ()
    { streamsize n = pptr () - pbase ();
      return (n && write_to_window (win, pbase (), n) != n) ? EOF : 0;
    }

    int windowbuf::overflow (int ch)
    { streamsize n = pptr () - pbase ();
      if (n && sync ())
        return EOF;
      if (ch != EOF)
        {
          char cbuf[1];
          cbuf[0] = ch;
          if (write_to_window (win, cbuf, 1) != 1)
            return EOF;
        }
      pbump (-n);  // Reset pptr().
      return 0;
    }

    streamsize windowbuf::xsputn (char* text, streamsize n)
    { return sync () == EOF ? 0 : write_to_window (win, text, n); }

    int
    main (int argc, char**argv)
    {
      window *win = ...;
      windowbuf wbuf(win);
      ostream wstr(&wbuf);
      wstr << "Hello world!\n";
    }

## 5.3 C-style formatting for `streambuf` objects

The GNU `streambuf` class supports `printf`-like formatting and scanning.

Method: int streambuf::form (const char *format, ...)
  Similar to `fprintf(file, format, ...)`. The format is a `printf`-style format control string, which is used to format the (variable number of) arguments, printing the result on the `this` streambuf. The result is the number of characters printed.

Method: int streambuf::vform (const char *format, va_list args)
  Similar to `vfprintf(file, format, args)`. The format is a `printf`-style format control string, which is used to format the argument list args, printing the result on the `this` streambuf. The result is the number of characters printed.

Method: int streambuf::scan (const char *format, ...)
  Similar to `fscanf(file, format, ...)`. The format is a `scanf`-style format control string, which is used to read the (variable number of) arguments from the `this` streambuf. The result is the number of items assigned, or `EOF` in case of input failure before any conversion.

Method: int streambuf::vscan (const char *format, va_list args)
  Like `streambuf::scan`, but takes a single `va_list` argument.

## 5.4 Wrappers for C `stdio`

A stdiobuf is a `streambuf` object that points to a `FILE` object (as defined by `stdio.h`). All `streambuf` operations on the `stdiobuf` are forwarded to the `FILE`. Thus the `stdiobuf` object provides a wrapper around a `FILE`, allowing use of `streambuf` operations on a `FILE`. This can be useful when mixing C code with C++ code.

The pre-defined streams `cin`, `cout`, and `cerr` are normally implemented as `stdiobuf` objects that point to respectively `stdin`, `stdout`, and `stderr`. This is convenient, but it does cost some extra overhead.

If you set things up to use the implementation of `stdio` provided with this library, then `cin`, `cout`, and `cerr` will be set up to use `stdiobuf` objects, since you get their benefits for free. See section 6. C Input and Output.

## 5.5 Reading/writing from/to a pipe

The procbuf class is a GNU extension. It is derived from `streambuf`. A `procbuf` can be closed (in which case it does nothing), or open (in which case it allows communicating through a pipe with some other program).

Constructor: procbuf::procbuf ()
  Creates a `procbuf` in a closed state.

Method: procbuf* procbuf::open (const char *command, int mode)
  Uses the shell (``/bin/sh'`) to run a program specified by command.

  If mode is ``ios::in'`, standard output from the program is sent to a pipe; you can read from the pipe by reading from the `procbuf`. (This is similar to ``popen(command, "r")'`.)

  If mode is ``ios::out'`, output written to the `procbuf` is written to a pipe; the program is set up to read its standard input from (the other end of) the pipe. (This is similar to ``popen(command, "w")'`.)

  The `procbuf` must start out in the closed state. Returns ``*this'` on success, and ``NULL'` on failure.

Constructor: procbuf::procbuf (const char *command, int mode)
  Calls ``procbuf::open (command, mode)'`.

Method: procbuf* procbuf::close ()
  Waits for the program to finish executing, and then cleans up the resources used. Returns ``*this'` on success, and ``NULL'` on failure.

Destructor: procbuf::~procbuf ()
  Calls ``procbuf::close'`.

## 5.6 Backing up

The GNU iostream library allows you to ask a `streambuf` to remember the current position. This allows you to go back to this position later, after reading further. You can back up arbitrary amounts, even on unbuffered files or multiple buffers' worth, as long as you tell the library in advance. This unbounded backup is very useful for scanning and parsing applications. This example shows a typical scenario:

    // Read either "dog", "hound", or "hounddog".
    // If "dog" is found, return 1.
    // If "hound" is found, return 2.
    // If "hounddog" is found, return 3.
    // If none of these are found, return -1.
    int my_scan(streambuf* sb)
    {
        streammarker fence(sb);
        char buffer[20];
        // Try reading "hounddog":
        if (sb->sgetn(buffer, 8) == 8
            && strncmp(buffer, "hounddog", 8) == 0)
          return 3;
        // No, no "hounddog":  Back up to 'fence'
        sb->seekmark(fence); //
        // ... and try reading "dog":
        if (sb->sgetn(buffer, 3) == 3
            && strncmp(buffer, "dog", 3) == 0)
          return 1;
        // No, no "dog" either:  Back up to 'fence'
        sb->seekmark(fence); //
        // ... and try reading "hound":
        if (sb->sgetn(buffer, 5) == 5
            && strncmp(buffer, "hound", 5) == 0)
          return 2;
        // No, no "hound" either:  Back up and signal failure.
        sb->seekmark(fence); // Backup to 'fence'
        return -1;
    }

Constructor: streammarker::streammarker (streambuf* sbuf)
  Create a `streammarker` associated with sbuf that remembers the current position of the get pointer.

Method: int streammarker::delta (streammarker& mark2)
  Return the difference between the get positions corresponding to `*this` and mark2 (which must point into the same `streambuffer` as `this`).

Method: int streammarker::delta ()
  Return the position relative to the streambuffer's current get position.

Method: int streambuf::seekmark (streammarker& mark)
  Move the get pointer to where it (logically) was when mark was constructed.

## 5.7 Forwarding I/O activity

An indirectbuf is one that forwards all of its I/O requests to another streambuf.

An `indirectbuf` can be used to implement Common Lisp synonym-streams and two-way-streams:

    class synonymbuf : public indirectbuf {
       Symbol *sym;
       synonymbuf(Symbol *s) { sym = s; }
       virtual streambuf *lookup_stream(int mode) {
           return coerce_to_streambuf(lookup_value(sym)); }
    };

Go to the first, previous, next, last section, table of contents.