April 91 - Reading C++ Interfaces
Reading C++ Interfaces
Eric M. Berdahl
Many of you have seen the traffic on MacApp.Tech$ discussing the relative merits of
C++, Pascal, Eiffel, and whatever else happens to work with MacApp-or will someday.
Each of you has chosen one or more languages in which to express your designs and has
gone happily forward.
For those using Pascal, everything has been rather simple. Since MacApp is written in
Pascal, the compiler takes care of almost everything. The rest of us learned a little
about the "magic" that Pascal throws under our feet, and went happily on our way.
Everyone was happy.
But every happy story needs a plot twist to make it really interesting. The particular
twist that faces us now involves C++, MacApp, and Apple itself. If you haven't heard it
yet, listen up: at the MADA conference in February, Apple announced that MacApp 3.0
is being written in C++.
After the bomb dropped, the dust settled, and the damage assessments began, we learned
that Pascal wasn't yet dead and buried. However, we all began to realize that knowing a
bit of what's out there besides Object Pascal might not be a bad idea.
Come on into the Kitchen!
Welcome to The Soup Kitchen. As with soup left alone too long, our community has
settled into various layers with little interlayer interplay. Thus, today's menu
features a Pascal layer and a C++ layer, but never the 'twain do meet. As any cook
knows, such soups have little taste-so The Soup Kitchen will stir things up a bit, by
exploring the uncharted realms of MacApp programming.
The ability to work with C++ is a dish I think you will enjoy-or at least tolerate-once
you see a bit about how it works and what goes into the pot. So for my first series of
columns, I'll address the needs of the non-C++ community to work with MacApp code
written in C++. Since it is widely accepted that one must read MacApp source code
sometimes, the first goal will be to give everyone a basic reading knowledge of C++.
Next, to address the large audience that has a need to modify MacApp, I'll show you how
to modify C++ code. Finally, some of you will want to catch the wave and change to C++
altogether. This will be my eventual topic also.
Today, let's look at what you might find in C++ interfaces.
Inheritance and Polymorphism
The term "object programming" carries a lot of weight. Depending on who you're
talking to, you'll hear about things like garbage collection, stack objects versus free
store objects, and exception handling. However, to do "object programming," you need
only two things-just inheritance and polymorphism, nothing else. In fact, these are
the only two object concepts provided by Apple's Object Pascal language [1] . That is,
Object Pascal allows you to write classes which inherit from superclasses and to
override methods of superclasses. This is a natural place to begin learning to read
C++.
C++ Class Declarations
Listings 1 and 2 contain excerpts from the Nothing sample program provided with
MacApp 2.0.1. They are equivalent Pascal and C++ versions of the TNothingApplication
object. TNothingApplication is a simple class, but it will show many of the basics of
class declarations in C++.
class TNothingApplication : public TApplication {
This line tells us we are beginning the declaration of a class called TNothingApplication
which inherits from TApplication. Everything between the { and its matching } is the
class declaration. Simple, right? Ok, says the quick reader, but what does that public
keyword mean, and what exactly is the significance of the colon? Good questions. The
basic form of the class statement is:
class : public {
};
A colon following the name of the class indicates that the class inherits from something
[2] . The public keyword used in this location is a bit more difficult to explain without
confusing the novice further than necessary. For now, we'll just say you always want
to use it as you see it above.
Keyword-public
class TNothingApplication : public TApplication {
public:
Here's that funny public keyword again, so it's time to explain a bit of the magic of
C++. Since one of the tenets of object programming is data hiding, C++ provides a
compiler-enforced system for hiding data within objects. Features of the class
declaration following public: are visible to everyone. Thus, anyone has access to them
and can use them.
In contrast, sections of the class declaration following private: are visible only to
methods of the class. Thus, only methods of the class can use private features. No one
else, not even a method of a subclass, has access to private features. (Compare this to
Object Pascal; it has no such data hiding syntax, at least not until '9x comes around, so
everyone has access to everything about the class.) Sections of public: features and
sections of private: features can be mixed freely in a class to denote the relevant access
of any particular feature.
Let's go back and look at the public keyword in the first line of the class declaration.
What the public keyword means here is that all the public features of the superclass
should be public for the new class also. If the inheritance was private, the client
(something which uses a particular object) would interact with our class only through
our features, and not through anything our superclass does. As I said before, you will
probably use public inheritance exclusively.
Keyword-protected
Another protection offered by C++ is protected:. Protected features of a class are
visible to the class and its immediate subclass. If a class inherits publicly, protected
features of the superclass are protected features of the derived class. Inheriting
privately makes public features of the superclass private features of the derived class.
And, before you ask-no, you can't inherit "protectedly.
Comment syntax and method declaration
class TNothingApplication : public TApplication {
public:
// Initializes the application and globals.
virtual pascal void INothingApplication(
OSType itsMainFileType);
The // token is a comment delimiter. Everything between it and the next return
character is a comment. The following line is a declaration of a method of
TNothingApplication. The method's name is INothingApplication, and it has one
argument, itsMainFileType, of type OSType. (Remember that C++ uses the C-style
argument declarations, so the type precedes each argument.)
Keyword-virtual
The virtual keyword used in this position indicates that the method will be
polymorphic. Since Pascal only knows about polymorphic methods, and our goal is to
be usable from and linkable to Pascal, all our methods should be declared virtual [3].
The pascal void construct
The pascal void construct is a little easier to explain. The pascal keyword indicates that
the method will use Pascal calling conventions (as opposed to C calling conventions).
You probably always want to use this since Pascal cannot emulate other calling
conventions. Since every routine in C++ is a function (all routines have the ability to
return a value), void is the way of syntactically saying that a function returns
nothing. This is equivalent to declaring a PROCEDURE in Pascal. Naturally, if the
routine (or method, as the case may be) actually is meant to return something, the
return type of the routine would be substituted for void.
Comment your overrides
Unlike Pascal, C++ does not have an OVERRIDE keyword. As a matter of style, many
style guides and C++ programmers-myself included-recommend tagging all overrides
with a comment like "// OVERRIDE" to indicate that you are overriding the method.
class TNothingApplication : public TApplication {
public:
// Initializes the application and globals.
virtual pascal void INothingApplication(
OSType itsMainFileType);
};
With the final } and the semicolon, our class declaration is complete.
Instance variables
Instance variables are the only thing missing from our treatment of class declarations.
Listings 3 and 4 show a C++ class that looks just like TNothingApplication with a few
instance variables, and its Pascal correlate.
class TNewNothingApplication : public TApplication {
public:
short fAnInteger; // Integer instance variable
long fLongInt; // LongInt instance variable
char fAChar; // Char instance variable
protected:
TObject* fATObject; // TObject instance variable
// Initializes the application and globals.
virtual pascal void INothingApplication(
OSType itsMainFileType);
};
Notice that instance variables are declared using the C-style syntax of putting the type
before the variable name. And that the C++ types short, long, and char correspond to
the Pascal types Integer, LongInt and Char.
The only real brain stretcher here is the TObject* fATObject declaration. Read
literally, this declaration says fATObject is of type pointer to TObject. In Pascal, the
compiler takes care of dereferencing, so all objects look just like regular
variables-hence the equivalent fATObject: TObject.
In C++, variables which are Pascal objects look like pointers to variables; hence the
TObject* syntax. As you'll see later, you manipulate Pascal object variables just like
pointers in the same way that Pascal manipulates object variables just like variables.
Notice that TNewNothingApplication uses the private: and protected: access features of
C++. In this example, fAnInteger, fLongInt, and fAChar are all private, so only
TNewNothingApplication methods will be able to access them. The fATObject instance
variable is protected and will only be visible to TNewNothingApplication and its
descendants.
It's important to realize that the equivalent Pascal declaration is oblivious to the
access restrictions C++ has placed on the class' instance variables and methods. This
is because the access restrictions the C++ compiler enforces are syntactic only; they
don't have any effect on the object code produced by the compiler. Thus they don't affect
our ability to link with Pascal in the slightest.
Declaration of Constants and Types
There are a lot of constants declared in MacApp interfaces. So, how do you declare
constants in C++? Persons familiar with standard C code will recognize #define
statements as macro definitions and point to these as methods of declaring constant
values, but C++ provides a better mechanism-the const facility. It works like this:
take any variable declaration (i.e. short kSomeConstant;), put the const keyword in
front and give it a value (i.e. const short kSomeConstant = 1;) and you have declared a
constant value.
This has several advantages over the C-style #define mechanism and the Pascal CONST
declarations. The C++ constant is given an explicit type, whereas the the other
language's constants have implicit types assigned by the compiler. In Pascal, the
intended type is often obvious to the reader anyway, but the const declaration in C++
allows anything to be a constant-strings, objects, records, you name it.
Finally, it's necessary to define types in terms of other types and to define non-object
data structures. The first task is done by typedefs:
TYPE Mask = INTEGER; { Pascal type declaration }
typedef short Mask; // C++ type declaration
These two lines of code define the type Mask to be equivalent to a 16 bit word (i.e.
INTEGER in Pascal and short in C++). The second task is handled by struct
declarations. Pascal RECORDs and C++ structs look virtually the same. The central
difference is the fact that the type precedes the field name.
Rect = RECORD
top: INTEGER;
left: INTEGER;
bottom: INTEGER;
right: INTEGER;
END;

struct Rect {
short top;
short left;
short bottom;
short right;
};
Looking for Feedback
Now you know the basics of reading C++ interface declarations in MacApp code and
understand how to correlate these with Pascal. In the future, we'll cover more of the
same and in greater depth.
This column isn't for me, for Apple, or for MADA-it's for you, the reader. I hope you
like it. Feedback, questions, and suggestions for future directions and topics are
encouraged at my AppleLink address. Who knows, you may end up being the subject of a
column! n
Footnotes:
1. One of my reviewers remarked that I was selling Pascal and other
languages short by omitting other "object concepts" such as encapsulation.
While such features are certainly part of the object programming tradition,
my purpose here is really to separate object programming from other
paradigms (e.g. structured programming) which also provide encapsulation
yet are not considered object-oriented.
2. and if you want to be usable from Pascal code, your C++ classes must
always inherit from something. Since Pascal's method dispatching is very
different from C++, Apple's implementation of C++ includes the PascalObject
class. Classes that inherit from PascalObject will use Pascal style method
dispatching and are usable from Object Pascal code. Now, does this worry
you? Probably not, since most (if not all) of your classes descend from
TObject. Guess what TObject inherits from? PascalObject.
3. For the technically masochistic: virtual indicates that a method will be
polymorphic-it will bind at runtime. If we do not declare a method as virtual,
the compiler would perform compile-time binding of the method call based on
the static type of the object. Compare this to Pascal, which only allows
dynamic binding. The take-home lesson: use virtual if you want your methods
to be accessible from Pascal. Likewise, any methods of Pascal objects must be
declared virtual so C++ can access them. Generally, you want all your
methods to be virtual anyway, right? So, just use it and be happy.