Pseudo Objects
Volume Number: 5
Issue Number: 8
Column Tag: C Objects
By Adam Treister, Santa Barbara, CA
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
Adam Treister is the president of For Your Information in Santa Barbara, CA,
and, when not spending too much time writing this article, is putting the finishing
touches on Commander Bond’s Briefcase, a collection of vertical market and small
business calculations. He would love feedback on the article, and to have others
implement more drawing features and send them to AppleLink D3474.
Evolution of the Macintosh
I recently attended one day of the Apple Worldwide Developers Conference 89, and
quickly caught the thrust of Apple’s unambiguous message to developers. To quote one
of the slides (and Greg Williams) “If you don’t learn object-oriented programming
now, you will not be able to program the Macintosh later.” There is not a lot of room
for interpretation there. When C++ becomes available, a large number of C
programmers will be switching to it. With the standardization offered by a joint
development between Apple and AT&T, the nice extensions C++ adds to straight C
programming, the support of Object Oriented Programming and the object libraries
available through MacApp and other sources, C++ seems to be destined to become a
standard Macintosh language.
From what I heard, few developers were arguing with Apple’s hard line support
of Object Oriented Programming. After all, the evolution of the Macintosh is looking
pretty solid. The Mac is reaffirming its roots in the object oriented environment of
the Xerox Star. It is striving to become a large scale operating system with virtual
memory and (someday) true multi-tasking, and it is moving to a software
environment that allows maximum flexibility and maintainability. And, a few years
down the road, just when OS/2 is finally overcoming the obstacles of its own weight
and poor design, the Macintosh, on the foundation of a (then) completely object
oriented software base, will be in a position to make a seamless transition to
multiprocessor architectures, and blow the blue suits out of the water.
A Gentle Transition
With these dreams as our beacon, and the dogcow cheering us onward, developers
will look to C++ as a way to shift into object oriented programming without
sacrificing performance. Yet there seemed to be a collective angst amongst the C
programmers, that the transition to C++ will be a long and burdensome one and that
the costs will be high. Personally, I don’t expect the transition to be that
overwhelming. There is no complete rewrite necessary, as there is when switching
between two syntactically different languages. C++ includes all of C, and the compiler
will not be grading you on how object oriented your code is. The transition can be
gradual, at your own speed, and within your own style. The additional features of C++
should encourage good programming style, rather than choke on any of your existing
bad habits.
As part of my own transition to C++, I have written here a program in a
“pseudo” object oriented style. It is meant to demonstrate that object oriented
programming is as much a style of programming as a characteristic of a language. It is
also meant to put my code into a style such that, when I switch to C++, I will only need
to make some textual substitutions rather than to completely restructure my
applications.
True Object Oriented Programming
Definitionally, an object oriented language must satisfy all of the following
properties:
data abstraction
inheritance
polymorphism
dynamic binding
This program will implement the first three properties in conventional C code. I
will also discuss a possible implementation of the last property, though it will not be
implemented, both for the implementation problems as well as the conceptual
obscurity of dynamic binding. Because of that short-coming, the occasional complete
disregard for the objects when I felt I could speed up the code, and for the love of a good
acronym, we cannot claim this program matches the defintion of object-oriented
languages. Instead, I will call it Pseudo Object Oriented Programming.
In this code are actually two entities, which may be of interest to the reader. The
first is a small shell, handling the start-up, main event loop, and shutdown
responsibilities, and dispatching messages to the window objects who take over from
there. This POOPShell (consisting of the first two program modules) is much the
same event handling shell that I use in my commercial programs. It is almost a mini-
MacApp in a few pages of source code. The second part, POOPDraw is an actual
application, a MacDraw clone, which almost ranks as a real program. I hope that this
program may act as the starting point for your drawing programs or drawing portions
of larger programs. Although POOPDraw is only the starting point for larger
applications, I think there is a vast potential for adding graphical editing capablities to
a wide variety of programs, which may not ever get them if the programmer has to
start from scratch. Hopefully, this program will provide a module with the necessary
capabilities to let you add simple graphic editing to your applications. If this is the
case (and I hope you’ll let me know if it is), then we may together verify all the good
things Apple is preaching about the drop-in extensibility of object oriented
programming.
First, I will give a brief description of the organization of POOPShell and
POOPDraw, and then discuss how it fulfills (or does not) the criteria of object
oriented programming. There are many places where I will rely on conventions or
self-imposed practices to gain advantages that are inherent in true object oriented
languages. But the point of this article is that the concepts of C++ and Object Oriented
Programming are not really that new to most of you. You may need to learn the extra
syntax of an expanded language, but the philosophy of Object Oriented Programming
will probably end up looking like an elegant manifestation of all the good habits you’ve
been trying to maintain all along.
Objects and the POOPShell
An object, as used in this program, is a handle, just like in any other Macintosh
program. It is created like any other handle, through the standard routines of the
Memory Manager. I use one of my favorite macros called _GetHandleToRecord() which
takes as its argument the typedef of a structure and returns a handle to that many
bytes, after checking that all went well. The function which creates the new object
(poignantly called New) initializes the object to know its own identity, from that point
on, the object is held be responsible for all messages that might be passed to it. To the
program as a whole, all objects are of the generic type ObjectHandle, but once a
message is passed to this object, it will provide the access to the private workings of
its class.
The early Greeks are rarely credited for their work in Compiler Design, but if
they had been into it, Archimedes would have been quoted as saying: “Give me a good
jump table and I won’t have to carry this 2x4 around with me all the time.” The crux
of this article is that one well-placed switch statement can turn C into a sufficiently
object oriented language.
By convention, the first several fields in the structure of any object are reserved
for those which are common to all objects. Only the first field is truly necessary to
accomplish modularity of code, but I’ve also included a few other fields which I’ve
arbitrarily chosen as a list of fields I think all objects should have. The list is cut
down to the minimum for this article, but you can easily add your own standard fields
by modifying the macro. All standard fields are conveniently included in all objects by
condensing all of their declarations into a macro called _StdObjectFields, which all
objects must include at the top of their type declaration:
/* 1 */
/* The field decls common to all objects */
#define _FLD1 void (*dispatch)();
#define _FLD2 WindowPtr port;
#define _FLD3 short class;
#define _FLD4 int length;
#define _FLD5 Rect bounds;
#define _FLD6 long attributes;
#define _StdObjectFields _FLD1 _FLD2 _FLD3
_FLD4 _FLD5 _FLD6
/** This macro is included in the structure definition of all
objects. For example, a hypothetical object declaration might
appear: **/
typedef struct
{
_StdObjectFields
/* object specific fields go here */
} HypoObjRec, *HypoObjPtr, **HypoObjHandle;
The first field of all objects, and the one crucial to this whole implementation, is
a function pointer called dispatch. Upon creation of a new object, we initialize this
field to point to a function specific to this class of object. The function contains little
more than a switch statement, which looks at the parameter called Message, and
according to its contents, calls some other function. All of the object’s routines are
declared static (which I #define into private) and are known only to this particular
class of object. This establishes a single entry point to all of the methods for any given
object, much as the ListManager or Packages have a single trap and a selector field,
which determines the functionality within the trap call.
The ParmP parameter is a pointer to a long, which means who knows what it
points to. It is a wildcard pointer, which will mean different things in each function
call. If the call only has one parameter, ParmP points to it. If there are multiple
parameters, ParmP is an array of pointers. Surprisingly, the entire POOPDraw only
has one function with more than one parameter, and it has two parameters, and though
it is risky practice to send blind pointers around, the potential errors it causes are
easily recognized and not too pesky. (Yes, a hack is a hack.)
To dispatch any message, you simply call the function