IconEdit
Volume Number: 8
Issue Number: 5
Column Tag: C Workshop
IconEdit: A MacApplication in C++
Or, how I learned to stop worrying and love the GUI.
By Kaelin L. Colclasure, Scott AFB, Illinois
Note: Source code files accompanying article are located on MacTech CD-ROM or
source code disks.
I’d heard of MacApp long before I bought my first volume of Inside Mac, but I
wasn’t that impressed by the idea of an “extendable application.” For one thing, I was a
self-taught, experienced programmer and I didn’t need anyone to show me how to write
a simple application. And for another, using MacApp at that time meant using Object
Pascal, and I prefer C over Pascal. I wrote my own file indexing routines back then,
too. Yes, I was a pig-ignorant refugee from the dark ages of data processing.
Now, a few years older and a few volumes of Inside Mac wiser, I realize what a
fool I was. These days I don’t write anything without checking to see if it’s already
available first, and I acknowledge that the best programming language is the one that
lets you complete the task at hand in the least possible time. I still use C(++), but I
like to think I use it for the right reasons now.
Unfortunately, by the time I saw the object-oriented light coalesced about
MacApp’s figurative head, I had already invested too much in C to switch over to Object
Pascal. Thus, when Apple announced their intention to support C++ in MacApp 2.0, I
was more than a little enthusiastic.
It took a bit longer than expected, but MPW C++ finally arrived last October.
APDA received their initial inventory and shipped my copy (Federal Express, of
course) on the same day - my birthday. It was a sign. But enough about me, you’re
here to read about C++!
Apple’s Extensions
MPW C++ supports the usual extensions to Macintosh implementations of the
underlying C language (i.e., the pascal type modifier, direct function calls, and pascal
strings). In addition, some enhancements were added to the specification of C++ itself
in the form of two new base classes: HandleObject and PascalObject.
Handle-based classes simplify the use of the Toolbox Memory Manager. They can
be declared by subclassing the predefined base class HandleObject. For example:
class MyHandleObject : public HandleObject {
// Field and member function definitions
}
As implied by their name, handle-based objects are implemented using handles
instead of pointers. Because of this, they are restricted in certain ways. Handle-based
objects must be allocated by the new operator and referenced indirectly through a
pointer (such a reference is actually doubly-indirect, but thanks to C++’s operator
overloading the HandleObject class takes care of that particular detail for you). Also,
multiple inheritance cannot be used in classes derived from HandleObject.
The PascalObject base class makes it possible to use Object Pascal classes (such
as the MacApp class library) from C++, and vice versa. PascalObject, being a subclass
of HandleObject, shares that class’ restrictions and adds a few of its own. A Pascal class
can contain only virtual functions, and (naturally) all non-private functions must use
Pascal’s calling conventions.
class MyPascalObject : public PascalObject {
public:
virtual pascal void MyMethod (void);
}
Functions of Pascal classes cannot be overloaded (the message passing scheme
does not support C++’s “mangled” name strategy). Also, Pascal classes should not use
C++’s constructors or destructors, as these would not be automatically invoked by
Object Pascal.
Conversion Pitfalls
Given the above, it’s obvious that the software wizards at Apple have gone out of
their way to insure an easy transition from Object Pascal to C++. There are, however,
still a few items to watch out for.
Veteran C programmers are no doubt already aware of the way Pascal passes
structures- if it’s less than 4 bytes, by value, otherwise by reference (try explaining
that one in an introductory programming course). A classic example of this is passing
a Rect to one of the ROM routines:
VAR aRect : Rect;
FrameRect (aRect);
becomes in C:
Rect aRect;
FrameRect (&aRect);
Thankfully, the compiler will catch these and similar errors thanks to C++’s
type checking.
Another idiosyncrasy of Object Pascal lies in its mapping of object references.
Despite the fact that Object Pascal objects must be allocated with New, they are declared
as instances rather than as pointers. In keeping with that metaphor, references to
fields and methods of objects do not require dereferences. For example:
/* 1 */
VAR gApplication : TIconApplication;
New (gApplication);
FailNIL (gApplication);
gApplication.IIconApplication (kFileType);
C++’s implementation is more straightforward- Pascal objects are declared and
referenced using pointers.
/* 2 */
TIconApplication *gApplication;
gApplication = new TIconApplication;
FailNIL (gApplication);
gApplication->IIconApplication (kFileType);
A somewhat more formidable obstacle is presented by MacApp’s reliance upon
Pascal’s nested procedures for certain methods. A nested procedure in Pascal has access
to its enclosing procedure’s local variables. C++ (like C) doesn’t support nesting of
functions, so this is a bit of a problem. The solution is to pass a “static link” pointer
to the function that points to the would-be enclosing function’s local variables, like so:
/* 3 */
pascal void NestedFunction (short aParameter,
void *staticLink);
pascal void EnclosingFunction (void) {
short aLocal;
NestedFunction (13, &aLocal);
}
Experimentation would seem to indicate that the static link parameters of
functions to be called by MacApp must point within a valid stack frame, even if the
function simply ignores the parameter. Setting it to nil has caused the Mac to generate
addressing exceptions.
On to the Code
Programmers familiar with MacApp should find few surprises in the C++
version of IconEdit. Following Object Pascal’s convention, the source is broken up into