Bruce Eckel's Thinking in C++, 2nd Ed Contents | Prev | Next

Iostream buffering

Whenever you create a new class, you should endeavor to hide the details of the underlying implementation as possible from the user of the class. Try to show them only what they need to know and make the rest private to avoid confusion. Normally when using iostreams you don’t know or care where the bytes are being produced or consumed; indeed, this is different depending on whether you’re dealing with standard I/O, files, memory, or some newly created class or device.

There comes a time, however, when it becomes important to be able to send messages to the part of the iostream that produces and consumes bytes. To provide this part with a common interface and still hide its underlying implementation, it is abstracted into its own class, called streambuf. Each iostream object contains a pointer to some kind of streambuf. (The kind depends on whether it deals with standard I/O, files, memory, etc.) You can access the streambuf directly; for example, you can move raw bytes into and out of the streambuf, without formatting them through the enclosing iostream. This is accomplished, of course, by calling member functions for the streambuf object.

Currently, the most important thing for you to know is that every iostream object contains a pointer to a streambuf object, and the streambuf has some member functions you can call if you need to.

To allow you to access the streambuf, every iostream object has a member function called rdbuf( ) that returns the pointer to the object’s streambuf. This way you can call any member function for the underlying streambuf. However, one of the most interesting things you can do with the streambuf pointer is to connect it to another iostream object using the << operator. This drains all the bytes from your object into the one on the left-hand side of the <<. This means if you want to move all the bytes from one iostream to another, you don’t have to go through the tedium (and potential coding errors) of reading them one byte or one line at a time. It’s a much more elegant approach.

For example, here’s a very simple program that opens a file and sends the contents out to standard output (similar to the previous example):

//: C18:Stype.cpp
// Type a file to standard output
#include "../require.h"
#include <fstream>
#include <iostream>
using namespace std;

int main(int argc, char* argv[]) {
  requireArgs(argc, 1); // Must have a command line
  ifstream in(argv[1]);
  assure(in, argv[1]); // Ensure file exists
  cout << in.rdbuf(); // Outputs entire file
} ///:~

After making sure there is an argument on the command line, an ifstream is created using this argument. The open will fail if the file doesn’t exist, and this failure is caught by the assert(in).

All the work really happens in the statement

cout << in.rdbuf();

which causes the entire contents of the file to be sent to cout. This is not only more succinct to code, it is often more efficient than moving the bytes one at a time.

Using get( ) with a streambuf

There is a form of get( ) that allows you to write directly into the streambuf of another object. The first argument is the destination streambuf (whose address is mysteriously taken using a reference, discussed in Chapter XX), and the second is the terminating character, which stops the get( ) function. So yet another way to print a file to standard output is

//: C18:Sbufget.cpp
// Get directly into a streambuf
#include "../require.h"
#include <fstream>
#include <iostream>
using namespace std;

int main() {
  ifstream in("Sbufget.cpp");
  assure(in, "Sbufget.cpp");
  while(in.get(*cout.rdbuf()))
    in.ignore();
} ///:~

rdbuf( ) returns a pointer, so it must be dereferenced to satisfy the function’s need to see an object. The get( ) function, remember, doesn’t pull the terminating character from the input stream, so it must be removed using ignore( ) so get( ) doesn’t just bonk up against the newline forever (which it will, otherwise).

You probably won’t need to use a technique like this very often, but it may be useful to know it exists.

Contents | Prev | Next


Contact: webmaster@codeguru.com
CodeGuru - the website for developers.
[an error occurred while processing this directive]