Feb 91 Letters
Volume Number: 7
Issue Number: 2
Column Tag: Letters
Enough Bus Errors 
By Kirk Chase, Editor
Mr. Bus Error
David Dunham
Seattle, WA
Aargh! I’ve had it! Enough bus errors! As a developer, I like to reduce my
number of bugs, so I keep Mr. Bus Error running (more later). As a user, I like to run
programs. I don’t like them generating bus errors, having to reboot without Mr. Bus
Error and rerunning the program. I’ve had to do this with software from Aldus, Apple,
Ashton-Tate, and Microsoft (to name just a few large companies).
With the Macintosh’s double-pointer memory handles, it’s easy to dereference a
NIL handle. Consider this code:
/* 1 */
h = NewHandle(sizeof(dataStruct));
(**h).field = 0;
The problem, as should be obvious, is that NewHandle() may fail if the heap is
too full or fragmented. Before we use a handle, we should test it:
/* 2 */
if (h != NIL) /* Is it valid? */
But we may have a lot of NewHandle()s (or GetResource()s) in our program.
It’s easy to miss one. What happens if we do? Both NewHandle() and GetResource()
return NIL (0L) if they can’t return a valid handle. We then try to double-dereference
this. *h is *(0L), the contents of location 0. We have no way of knowing what’s there
(it’s usually set by Finder when an application starts, but of course the Macintosh is a
multi-tasking machine, with other applications, drivers, and GetNextEvent() patches,
any of which could change it on purpose or through their own bug). **h is **(0L), or
whatever location 0 points to. We really have no idea what location 0 points to. So
(**h).field = 0; has just trashed some unknown address. This may never hurt us
(since there are so many possible addresses), which is why so many errors of this type
are released. But this is just gambling that location 0 will always contain some
benevolent value. I wouldn’t bet my data on that.
How can we catch dereferencing a NIL handle? We end up using the contents of
location 0 as an address, so the simple solution is to put an invalid address value at
location 0. Mr. Bus Error is a fine INIT that does just this (once per VBL interrupt, if
my memory’s correct). The value it uses is 0xF0F0F1. If used as an address, this
generates a bus error on 68020 and 68030 machines. On a 68000, it generates an odd
address exception. Simple. Easy. No work on your part (other than to locate Mr. Bus
Error; it’s on any developer CD-ROM from Apple and no doubt many other places). No
more will you forget to test for NIL handles and make my life miserable (and your own,
as you try to find those intermittent crashes -- the ones from f = (**h).field are
really fun).
Peter Polash came up with a technique that’s even better, though it does involve
actual work. Each time through your event loop, test what’s at location 0. If it’s not
what you set it to, you know you’ve changed it. Otherwise, you set it to a value like
0xF0F0F1. This gives you exactly the same error-finding functionality as Mr. Bus
Error, with the added bonus of being able to tell you if you ever set something in a
purged handle ((**h).field = 0; since *h is NIL for a purged handle) or NIL pointer,
and (to within one event) when you set it. You may have minor compatibility problems
during testing with this technique, since you’ll probably uncover other tasks changing
location 0 on you.
Please use NIL handle discipline (which brings up the point that many debuggers
can help you, too). Other error schemes aren’t sufficient (one unrunnable program
was written with MacApp).
A Small Error
Allen Stenger
Gardena, CA
Thank you for publishing the interesting and clearly-written article by Dennis
R. Cohen on LZW compression (October 1990). MacTutor needs more short articles
like this one, that illustrate a technique without trying to develop a full-blown
application.
I would like to point out a small error in the code which may trouble your
readers: the compression program produces incorrect results if it overwrites an
existing file. The format of the output file is a 56-byte header, followed by the
compressed data. When opening a new output file, the routine GetOutputFile writes a
dummy block for the header, which will later be filled in with the real header data; in
the meantime the compressed data will be written following the dummy header. But
when opening an existing file, no dummy header is written, so the compressed data is
written from the very beginning of the file and then the first part is overwritten with
the header.
This may touch on a question of style. The compression and decompression
programs use an “economical” approach of reusing an existing file, rather than the
“brute force” approach of deleting an existing file and always starting from scratch.
Which is better? Because the economical approach splits the processing into two paths,
it may be more error-prone (as shown by the error that did occur, above; also note
that the file creator and file type of the existing file are left unchanged, rather than
being set to the values for new files). Does the economical approach have any
advantages?
Revised DrawMyStuff.c
Chip Zempel
Fair Oaks, CA
Note: Source code files accompanying this letter are located on MacTech CD-ROMor source code disks.
Thanks for Mark B. Kauffman’s introduction to using the THINK C Class Library
in the September ‘90 issue. It finally made me get off my butt and get my feet wet in
OOP, if I can mix my metaphors a bit.
I made some changes to his code that illustrate one of OOP’s best features --
polymorphism, or the ability of a subclass to override an ancestor’s methods -- and I
thought you might like to share them with your other readers who are just learning
OOP.
Kauffman’s DrawMyStuff() sets up a line, an array of four rectangles, and an
array of four ovals. Then he sends them various messages to set their locations and
draw and erase themselves. In a “real world” drawing program, of course, you
wouldn’t know beforehand what type of objects the user would want to create. Your
program would probably allocate a big array of pointers to CShapes (the parent class)
and then stuff in whatever specific shapes the user selected at run time.
My revised DrawMyStuff() has just one big generic array of CShapes, and sends
them all generic locate, draw, and erase messages. Note that the only part of my code
that “knows” whether each shape is a rectangle, line, or oval is the section that
initializes each object. (The user would do this in a real program.) Everything else
-- setting locations, drawing, erasing, and deleting -- is done “blind.” My version
doesn’t know what object is there, and it doesn’t need to. It just says “draw yourself,”
or whatever, and the object responds to the message it receives with the method
implemented for its subclass.
The final result looks identical to the original when the screen is drawn. (Note
however, that I drew and erased the line object in a different order, so that I could send
all nine objects their Draw() message in the same “for” loop.) Here’s the code (and
please keep running more THINK Class Library tutorials!):
/* 3 */
/*
* Source - DrawMyStuff.c
* based on Mark B. Kauffman's article
* in the 9/90 MacTutor
*
* revised to demonstrate polymorphism
* -- the way a subclass can override
* its ancestor's methods
*/
#include "oops.h
#include "CShape.h
void DrawMyStuff()
{
int i;
/* set up a big generic array */
CShape *myShape[9];
/* let's create 4 rectangles... */
for (i=0; i<4; i++)
myShape[i] = new(CRectangle);
/* ... one line... */
myShape[4] = new(CLine);
/* ... and 4 ovals */
for (i=5; i<9; i++)
myShape[i] = new(COval);
/* place the rectangles */
myShape[0]->SetShapeLoc(200,100,300,200);
myShape[1]->SetShapeLoc(210,110,290,190);
myShape[2]->SetShapeLoc(220,120,280,180);
myShape[3]->SetShapeLoc(230,130,270,170);
/* place the line */
myShape[4]->SetShapeLoc(100,50,300,50);
/* place the ovals */
myShape[5]->SetShapeLoc(100,100,200,200);
myShape[6]->SetShapeLoc(110,110,190,190);
myShape[7]->SetShapeLoc(120,120,180,185);
myShape[8]->SetShapeLoc(130,130,170,170);
/* here's the fun part! send each shape */
/* a Draw() message, generically */
for (i=0; i<9; i++)
myShape[i]->Draw();
/* relocate the line and Erase() */
myShape[4]->SetShapeLoc(210,110,290,110);
myShape[4]->Erase();
/* delete everything, again generically */
for (i=0; i<9; i++)
{
delete(myShape[i]);
}
}
/* end of listing - DrawMyStuff.c */
P.S. I found a terrific book on OOP -- Object Oriented Program Design, With
Examples in C++ by Mark Mullin, from Addison-Wesley. The reader gets to look over
his shoulder as he develops a inventory-personnel-accounting database application
using object-oriented techniques. He explains why he makes the choices he makes,
presents the special features of OOP languages (such as abstraction, encapsulation, and
polymorphism) as they become useful, and lays out general rules (and some
exceptions!) for good OOP practice. Definitely worth a look!
HyperDA II and HyperEngine 2.0
Symmetry Software
SCOTTSDALE, AZ
HyperDA II , Symmetry Software’s HyperCard stack access desk accessory,
begins shipping in December. HyperDA II will support major HyperCard™ 2.0 features
such as stylized text, multiple, scrollable, resizable windows, “Hot Text”, visual
effects and sound. Support for selection tools that allow users to copy graphics and text
to the clipboard are built in also. HyperDA II will support either HyperCard 1.x or 2.0
file formats, and you can even have both running in seperate windows on the desktop at
the same time.
Using HyperDA II, the enduser gains immediate access to stacks via the Apple DA
menu, while bypassing the large memory requirements of MultiFinder and HyperCard.
Users enjoy the same browse, edit and print tools found in HyperCard.
Registered users of HyperDA version 1.x can obtain an update for $29 (includes
s/h) by calling Symmetry’s Customer Service department. The upgrade is FREE (with
proof of purchase plus $5 shipping), if HDA 1.x was purchased on or after November
10, 1990. HyperDA II’s manufacturer’s suggested retail is $129.
Supporting the enhanced HyperCard™ 2.0 command set, HyperEngine 2.0,
Symmetry Software’s stack access development tool, is due for release Q1’91.
HyperEngine will support major features such as stylized text, multiple, scrollable,
resizable windows, “Hot Text”, visual effects and sound. Support for selection tools
that allow users to copy graphics and text to the clipboard are built in also. The
technology is based on Symmetry’s HyperDA II Software and has been licensed to other
developers since 1988.
Supporting MPW, Think C and Assembly languages, HyperEngine allows the
developer to build stack access into their applications. The enduser gains immediate
access to stacks while bypassing the large memory requirements of MultiFinder and
HyperCard.
A HyperEngine developer’s tool kit is sold by Symmetry that provides developers
with the resources to add stack access in their own applications. Help, tutorial or
reference stacks can be designed with the HyperCard environment, saving developers
the need to use valuable programming resources. You can obtain the developer kit for
$125 USD, plus shipping and handling direct from Symmetry Software.
Symmetry Software Corporation can be reached at 8603 East Royal Palm Road,
Ste 110, Scottsdale, Arizona, 85258. 602-998-9106.
TextWare Technology Now Available
TextWare Corporation
P.O. Box 3267
Park City, UT 84060
(801) 645-9600
(801) 645-9610 (FAX)
TextWare Corporation has now released its technology for the Macintosh.
TextWare brings full text indexing and retrieval capabilities to the Macintosh. It is
fully compatible with TextWare for PC and LAN platforms for those working in a mixed
environment. TextWare is ideal for indexing large volumes of information such as
those found on servers or CD-ROMs. The TextWare Toolkit is available to developers
for those wishing to add TextWare capability to their products. Contact TextWare for
more information.