Introduction
Suppose you have an object of type Temperature (see the sidebar for some reasons why you might want to have a class such as Temperature). Now suppose you want to output it. That should be as simple as
os << t;But what scale is used to print out the value of t? If you look at the header for class Temperature in Listing 1, you will see that I have been reasonably careful to hide the actual scale used internally by a Temperature object. If a client wants to extract Temperature, they have to explicitly specify the scale. You could generate the output this way:
os << t.inF();but this means that the output is always going to be Fahrenheit. What if the user wants something else? What if the user wants to change things "on the fly"? This column is about how to use some of the more advanced features of the IOStreams library to provide this type of flexibility.
This column is another tutorial. Ordinarily I try to leave tutorials to others and concentrate on real-world examples, but I think that this topic needs the exposure. What I am going to talk about here is that part of the IOStreams library that is often known as the iword/pword facility. I can say with some assurance that this is one of the most underutilized facilities of the IOStreams library. Just in case you have never heard of iword/pword, let me point out that this is not new to Standard C++, but was part of classical IOStreams as well. I first tried to use the facility on a project in 1995. When I did, I ran into a bug in the library implementation that I was using. Then I discovered that the reference I was using, (Steve Teales' Iostream Handbook [1]) actually mentions the bug I ran into. Here was a C++ library from a major vendor that had contained a bug in the implementation for so long that it was documented in a popular book, and several years after the publication of the book, I still found the same bug. Obviously this was not a heavily used facility. Nevertheless, the facility is part of the Standard IOStream library, and I think it is both necessary and useful. This column attempts to explain why I think so by showing you some simple examples of how to use the facility.
First, let me mention some of the other information that is out there about this facility. Bjarne Stroustrup [2] mentions the facility just briefly (a couple of paragraphs). He concludes with the statement "This storage and callback mechanism is fairly obscure. Use it only when you absolutely need to extend the low-level formatting facilities." Nicolai M. Josuttis devotes about two-and-a-half pages to the subject in his C++ Standard Library: Tutorial and Reference [3]. So far, the most in-depth tutorial that I have actually read is in Angelika Langer and Klaus Kreft's Standard C++ Iostreams and Locales [4], which has to be considered a rather specialized reference.
I can not argue that the facility has its obscure points, especially when the callback mechanism is used. Unfortunately this is true about many aspects of C++ and its library. Like the vast majority of C++, however, you do not have to worry about the complexity unless you actually need to use the facility. Furthermore, the complexity is only of concern to the class developer. Provided he or she does their job correctly, the clients of the facility do not have to even know it exists. On the whole, I think a little thought and some experimentation are sufficient to figure out most of the obscurities. With that said, I still have to recommend that you get one of the last two references mentioned above (I assume you already have the first), just to avoid having to learn everything the hard way.
I think that more class designers should look into the iword/pword facility. I come across classes all the time that could use a little extra control of their output facility. This is true even if the only reason they have an operator<< function is to display values for debugging. More often, I see classes that generate output in some required format that could use a different format for generating debugging output. The ability to set a single boolean flag in the ostream would be a nice addition to the usability of such classes. As I hopefully will show, it isn't that hard.
I first discovered the iword/pword facility because I went looking for it. I had written a class which I wanted to be able to format the output in different ways (not surprisingly it was a Date class). IOStreams provides facilities to control the formatting of built-in types, as well as manipulators to facilitate the use of those facilities. The information used by these facilities is stored as part of the stream. I reasoned that IOStreams had to provide some way to allow formatting information to be added to the stream for user-defined types. This was just in keeping with C++'s philosophy of being able to add user-defined types to the language that behaved exactly like the built-in types. Knowing it had to be there and what I wanted to do with it allowed me to quickly figure out how the iword/pword facility worked.
To provide a quick summary (which the examples will hopefully make clear), each iostream conceptually has two arrays, one of type long and the other of type void*. The IOStreams library provides (in ios_base) two functions: iword(int) and pword(int). These take an index and return a reference to the corresponding long or void*. That part is simple. The next question is "where does the index come from?" A little thought will tell you that the index obviously needs to be unique to the class. One obvious place to get a unique identifier for a class is via the Run-Time-Type-Identification mechanism that is now part of Standard C++. The operator typeid returns a reference to a typeinfo object. This reference could be used to index a map or hash table. This is not the way it actually works in IOStreams, but I mention it to get you to think about the problem. Once you do so, you will more easily understand the actual mechanism.
Since IOStreams predates the RTTI mechanism in Standard C++, it provides its own facility for generating a unique identifier for any class that wishes to have one. This is the static function ios_base::xalloc. Each call to this function returns a new index. So, a class that wishes to use the iword/pword facility needs to call this function once and store the result someplace specific to the class. The obvious candidate is a class static variable that is initialized by a call to xalloc. Once you have an index that is unique to the class, you can use it to access the same iword or pword location in every iostream.
You now have two locations in every iostream that can be used to store class-specific formatting information. The actual interpretation of the information is up to the class or more specifically its operator<< and operator>> functions. Again the obvious candidates are to use the long(iword) to store flags or other simple information, and the void*(pword) can be used to reference anything more complicated that is desired. Most people immediately think of sticking a reference to a string or other structure containing advanced formatting information into the pword location, but that is not the only possibility. Allow me to continue my Temperature example.
User-Defined Class Output Formatting
We have a class Temperature, and we want to be able to output its value using one of three common temperature scales: Fahrenheit, Celsius, or Kelvin. Additionally, we would like to be able to optionally add the scale identifier. I had the additional requirement that I wanted to be able to derive a new class from Temperature to use a different scale and to be able to extend the user formatting functionality to support that new scale. The approach that I used is shown in Listing 1 (Temperature.h) and Listing 2 (Temperature.cpp). I will explain the iostreams section.
First, Temperature provides a static member strm_index of type const int to hold the index returned by xalloc. Next are the declarations for the manipulators. I am going to assume that you have at least a passing familiarity with manipulators, at least the standard ones such as flush and endl. These are just the same, except they are in class scope. Next come the declarations of the actual output functions. You can think of these as extended helper functions for operator<<. They are declared private since they are never called directly. The rest of Temperature.h is straightforward C++.
Since everything except the I/O facilities of class Temperature are provided as inline functions, the implementation file Temperature.cpp is entirely devoted to the iostreams functions.
First, we define strm_index and initialize it.
Next come the bodies of the manipulators. The first three are functionally similar: they just put the pointer to the associated output function into the pword location for the class. The next two handle the show_scale flag. I use the low order bit in iword as a bool to indicate whether to show the scale or not. Since this is the only flag I am using in class Temperature, I could have just used the entire iword value, but I show the general form where other flags may be present. This allows derived classes to add their own flags.
Next, I implement the output helper classes. These simply output the temperature value in the scale requested. You will see that I query the width value set for the field and make room for the scale character if one is expected. Other than that, the functions are almost trivial.
Finally, I provide the body of the operator<< function for class Temperature. It is fundamentally simple: it gets the pointer from pword (casting it from void* to the type required) and then checks to see if it is null. If there is no format function specified, the function fails. If there is a format function specified, then it is invoked.
I have concentrated on using the iword/pword facility to generate custom output formatting. Obviously, the same mechanism could be used on the input side as well. In this example, I have an extractor function that expects the scale to be specified (e.g., 100C) and fails if it is not. A more general purpose scheme would provide input helper functions and manipulators to set them, which would read a temperature in a given scale. But I will leave that as an exercise for the reader.
Summary
In this column, I have shown how to use the iword/pword facility of IOStreams to provide some simple custom formatting information for a user-defined class. I think you will agree that up to this point it hasn't been all that difficult. I will admit that this example avoids the need to use the register_callback capability of IOStreams, which helps keep it simple. (In a future column, I will show an example of callback usage.)
One nice aspect of this design is that it is extensible. If a class is derived from Temperature (see Sidebar), it can provide its own output helper function and the manipulator to set it, and the operator<< function for Temperature will use the new functionality. In fact, you don't even have to have a derived class: the user can add almost any desired functionality they want by simply creating the function to provide it.
As always, there are a number of details that might have alternate implementations. You will notice in operator<< that if there is no format specified, the stream state is set to ios::failbit, and the function returns without doing anything. This can be questioned. Forgetting to specify a format can be considered a programming logic error, not a problem with the stream. An alternative would be to throw an exception. This might happen when the stream state is set to failed, but it might not. By default, it will not throw. I have chosen to indicate the failure in the stream state because technically it is the stream that is missing the format information and hence is not usable for this object. Your design might prefer some other choice. In a future column, I will explore how to specify a default value for the formatting so that operator<< will not have an excuse to fail.
Another detail to be aware of is that if the iostream copyfmt function is called, the information in iword/pword is copied. Again, this seems correct to me, but an alternative would be to reset the values to their defaults. Doing this would require registering a callback function to be invoked whenever the copyfmt function was called.
All in all, the capabilities of the iword/pword facility strike me as another example of IOStreams extensible design.
References
[1] Steve Teale. C++ Iostreams Handbook (Addison-Wesley, 1993).
[2] Bjarne Stroustrup. The C++ Programming Language Special Edition (Addison-Wesley, 2000).
[3] Nicolai M. Josuttis. The C++ Standard Library: A Tutorial and Reference (Addison-Wesley, 1999).
[4] Angelika Langer and Klaus Kreft. Standard C++ Iostreams and Locales: Advanced Programmer's Guide and Reference (Addison-Wesley, 2000).
[5] Jack Reeves. "The (B)Leading Edge: Low Overhead Class Design," C++ Report, February 1998.
Jack W. Reeves is an engineer and consultant specializing in object-oriented software design and implementation. His background includes Space Shuttle simulators, military CCCI systems, medical imaging systems, financial data systems, and numerous middleware and low-level libraries. He currently is living and working in Europe and can be contacted via jack_reeves@bleading-edge.com.