Stream for All Seasons
Volume Number: 16
Issue Number: 4
Column Tag: Programming
A Stream for All Seasons
by Neil Mayhew, Calgary, AB
A lightweight ostream-compatible class using Pascal
strings
Getting Your Message Across
They say a picture is worth a thousand words, but a picture without any words is often
hard to figure out. The Macintosh user interface does a wonderful job of being
graphical, but it also uses text very effectively in appropriate places.
Can you imagine using a large application without a single word of text on its menus
and dialogs? Consumer electronics devices that use cute little icons for everything can
be extremely puzzling until you have spent a little time studying the manual-in text
form, of course.
The ability to manipulate text is therefore crucial to even the most graphical
Macintosh program. Text in the user-interface-rather than text as data-uses Pascal
string conventions. This doesn't always fit very well with C, which has it's own string
conventions. The problem is worse with C++, which has a much richer standard
library of text manipulation functions-none of which understand the Pascal string
format.
However, the power and flexibility of C++ can be used to overcome many of these
limitations in a way that is both transparent and economical. Yet again, templates,
inline functions, and stack-based objects can come to our rescue-if we know how to
take advantage of them.
This month's article describes a lightweight class that builds Pascal strings using the
<< notation used by the standard C++ ostream. Next month, I'll introduce a program
that uses STL-compatible iterators to monitor your System Folder for unexpected
changes to extensions and control panels by wayward installer programs.
Printf Considered Harmful
Much of the time, text that is being displayed in the user-interface is a fixed string
taken from a resource. Sometimes, though, text is built up from a number of pieces,
some fixed and some not. We have come to appreciate having the text of the Undo menu
item change to reflect the action that is being undone. In fact, there are all kinds of
situations in which user-interface text needs to be built up programmatically:
prompts, error messages, status messages, progress messages, and so on. In all of
these cases, the generated text needs to be inserted into the user-interface as a Pascal
string.
There are a number of simple approaches to doing this. For alerts and dialogs, the
ParamText mechanism is useful. Or, a sequence of string-concatenation operations can
be performed. Even better, one of the printf family of functions can be used to format a
string with complex substitutions via % characters.
Although the printf approach is powerful, it has some severe limitations in any
setting:
• substitutions are not type-checked or counted, and are therefore
error-prone
• sprintf's output string is not protected against overflow
In a Mac OS setting, there are some additional limitations:
• Pascal strings are not understood as arguments
• the output is a C string rather than a Pascal string
• printf cannot be used at interrupt time or in a code resource
• the C i/o library increases code size considerably
The C++ iostream library takes care of the first two problems very effectively, using
ostringstream, but the Mac OS-specific problems remain. How could we retain the
elegant << syntax and yet be able to handle Pascal strings with very low overheads?
Simple Is Beautiful
Early on in my programming career, I learnt that writing large, complicated
programs is relatively easy. What is hard is keeping them simple! A more experienced
colleague had a wonderful knack for developing small, sophisticated programs that
contained just enough functionality for the task in hand-and no more. The C++
iostreams library quite rightly has a huge range of functionality, much of which we do
not need for our limited task of formatting strings. If we select just the functionality
that is useful to us-concatenating strings, integers and floating-point numbers-it
should not be hard to implement a lightweight class that supports << syntax for those
items.
The output needs to be a Pascal string, and various manipulation capabilities for
Pascal strings will be needed. It makes sense therefore first to develop a C++
encapsulation of Pascal strings. This will simplify our stream implementation, and
will be a useful facility in itself.
As I mentioned in last month's article, the constant template-argument feature of C++
is specifically aimed at the implementation of fixed-length arrays such as Pascal
strings. This enables us to store various sizes of strings on the stack, instead of having
to allocate variable-length blocks from the heap. Without this, we would not be able to
meet our design criterion of usability at interrupt time or in a code resource.
Making It To First Base
Last month's template used only inline functions, and these were sufficiently simple
that they more or less disappeared in the generated code. The functions to process
strings are not so simple, however. If we make them inline, we face two possible
outcomes: either a substantial chunk of code will be inserted into every routine that
calls one of our template's methods, or the compiler will opt to make them non-inline
anyway.
What happens if we have non-inline methods in a template class? Ordinarily, we
would put the implementation of non-inline methods into a .cp file that is compiled and
linked. However, most development environments (CodeWarrior included) are not able