[comp.sys.mac.programmer] Casting in Think C

caw@munnari.oz.au (Chris Wright) (12/03/90)

Why do you have to cast gApplication in ThinkC e.g.:
extern	CApplication	*gApplication;

void main()

{
	gApplication = new(CEditApp);
	((CEditApp *)  gApplication)->IEditApp();
	gApplication->Run();
	gApplication->Exit();
}

if you remove the cast and try gApplication -> iEditApp(), the compiler
complains.
new is meant to "automatically assign a class to the newly created object"
e-mail me if this is too obvious to waste time on the net for!

Thanks

chris wright
  

phils@chaos.cs.brandeis.edu (Phil Shapiro) (12/04/90)

In article <6167@munnari.oz.au> caw@munnari.oz.au (Chris Wright) writes:
   Why do you have to cast gApplication in ThinkC e.g.:
   extern	CApplication	*gApplication;

   void main()
   {
        gApplication = new(CEditApp);
        ((CEditApp *)  gApplication)->IEditApp();
        gApplication->Run();
        gApplication->Exit();
   }

   if you remove the cast and try gApplication -> iEditApp(), the compiler
   complains.
   new is meant to "automatically assign a class to the newly created object"

There's really a couple things going on here.

When you use new() to create an object, the object is assigned a
number based on the type that's passed to new().  This is called the
Class ID.  When you call a method, this is the information that's used
to determine which method (if it's overridden) to call.  This is also
the information that bless() changes.

At compile time, the compiler must be happy that the method call (or
instance variable fetch) is correct.  This may or may not have
anything to do with the Class ID that's used at runtime.  This is how
a subclass can override a method, and have the overriding method
executed, even if the type the object is declared as is not of the
subclass's type <whew!>.

I tried mailing, but it bounced :-(

	-phil
--
   Phil Shapiro                           Technical Support Analyst
   Language Products Group                     Symantec Corporation
		Internet: phils@chaos.cs.brandeis.edu

nick@cs.edinburgh.ac.uk (Nick Rothwell) (12/05/90)

In article <6167@munnari.oz.au>, caw@munnari.oz.au (Chris Wright) writes:
> Why do you have to cast gApplication in ThinkC e.g.:
> extern	CApplication	*gApplication;
> 
> void main()
> 
> {
> 	gApplication = new(CEditApp);
> 	((CEditApp *)  gApplication)->IEditApp();
> 	gApplication->Run();
> 	gApplication->Exit();
> }
> 
> if you remove the cast and try gApplication -> iEditApp(), the compiler
> complains.

As it should. I'm not sure what the argument to new() actually is/does, but
my guess is that it just provides a size (a la "sizeof") to determine how
much store to claim.

gApplication is still a CApplication, and hence doesn't have an IEditApp()
method unless you cast it.

> new is meant to "automatically assign a class to the newly created object"

Now, I've never understood what this kind of thing means, ditto for Bless()
and all the rest. Presumably it does something automagical to the method
dispatcher. Anybody care to comment what "the class of an object" actually
means (as opposed to the static type of the object pointer)?

-- 
Nick Rothwell,  Laboratory for Foundations of Computer Science, Edinburgh.
                nick@lfcs.ed.ac.uk    <Atlantic Ocean>!mcsun!ukc!lfcs!nick
~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~
 "You ain't seen nothing yet. I can take this floor out too, no trouble."

caw@munnari.oz.au (Chris Wright) (12/06/90)

In article <3028@skye.cs.ed.ac.uk>, nick@cs.edinburgh.ac.uk (Nick Rothwell) writes:
> In article <6167@munnari.oz.au>, caw@munnari.oz.au (Chris Wright) writes:
> > Why do you have to cast gApplication in ThinkC e.g.:
> > extern	CApplication	*gApplication;
 

> gApplication is still a CApplication, and hence doesn't have an IEditApp()
> method unless you cast it.

	If my memory serves me correctly, gApplication is now a 
 	CEditApp. This can be checked with the member function.
	So it SHOULD have an IEditApp(). And the compiler 
	shouldn't complain.
	
	Though see the kind reply from Symantec earlier in this 
	thread (which I'm not sure if I understand!)

	chris.

phils@chaos.cs.brandeis.edu (Phil Shapiro) (12/06/90)

In article <3028@skye.cs.ed.ac.uk> nick@cs.edinburgh.ac.uk (Nick Rothwell) writes:
   In article <6167@munnari.oz.au>, caw@munnari.oz.au (Chris Wright) writes:
   > new is meant to "automatically assign a class to the newly
   > created object"

   Now, I've never understood what this kind of thing means, ditto for
   Bless() and all the rest. Presumably it does something automagical
   to the method dispatcher. Anybody care to comment what "the class
   of an object" actually means (as opposed to the static type of the
   object pointer)?

The only part of an object (in ThC) that indicates its class type is
the "Class ID".  This is a word-sized number assigned to each class at
compile time.  When you call a method, the method dispatcher, __msg(),
uses this number to determine which "method table" to use.  So all
bless() has to do is change this number, and voila!  It will behave
just as the bless()'d class, since it's using that class' method
table.

	-phil
--
   Phil Shapiro                           Technical Support Analyst
   Language Products Group                     Symantec Corporation
		Internet: phils@chaos.cs.brandeis.edu

claytor@tandem.physics.upenn.edu (Nelson Claytor) (12/06/90)

In article <6194@munnari.oz.au> caw@munnari.oz.au (Chris Wright) writes:
> In article <3028@skye.cs.ed.ac.uk>, nick@cs.edinburgh.ac.uk (Nick 
Rothwell) writes:
> > In article <6167@munnari.oz.au>, caw@munnari.oz.au (Chris Wright) 
writes:
> > > Why do you have to cast gApplication in ThinkC e.g.:
> > > extern      CApplication    *gApplication;
>  
> 
> > gApplication is still a CApplication, and hence doesn't have an 
IEditApp()
> > method unless you cast it.
> 
>         If my memory serves me correctly, gApplication is now a 
>         CEditApp. This can be checked with the member function.
>         So it SHOULD have an IEditApp(). And the compiler 
>         shouldn't complain.

At runtime, yes, gApplication is a CEditApp, and therefore it 
does have the IEditApp() method. However, at compile time, all 
the compiler knows is that you declared it as a CApplication. 
Therefore, *as far as the compiler is concerned*, there is 
no IEditApp.

Nelson

Nelson Claytor
claytor@tandem.physics.upenn.edu

ech@cbnewsk.att.com (ned.horvath) (12/07/90)

In article <6167@munnari.oz.au>, caw@munnari.oz.au (Chris Wright) writes:
> Why do you have to cast gApplication in ThinkC e.g.:
> extern	CApplication	*gApplication;

In article <3028@skye.cs.ed.ac.uk>, nick@cs.edinburgh.ac.uk (Nick Rothwell) writes:
> gApplication is still a CApplication, and hence doesn't have an IEditApp()
> method unless you cast it.

From article <6194@munnari.oz.au>, by caw@munnari.oz.au (Chris Wright):
> 
> 	If my memory serves me correctly, gApplication is now a 
>  	CEditApp. This can be checked with the member function.
> 	So it SHOULD have an IEditApp(). And the compiler 
> 	shouldn't complain.

> 	Though see the kind reply from Symantec earlier in this 
> 	thread (which I'm not sure if I understand!)

In Think's (Object) C, as well as in C++, the members (data fields or
messages) you can refer to depend upon the "static," or declared, type
of the object reference.  Notice I said "can refer to," i.e. the
compiler will complain: it must generate code based upon what every
CApplication -- not just CEditApp -- can do.

In this case, gApplication is a reference to a CApplication, so the
available members are those of a CApplication.  If you "know" it's a
reference to a specific subclass, you can still get at the subclass's
special messages by casting:

	if (member (gApplication, CEditApp)) {
		((CEditApp*)gApplication)->IEditApp(...);
	} else {
		gApplication->IApplication(...);
	}

The rules aren't arbitrary: YOU can see the "new (CEditApp)", but the
compiler only depends on the declaration.

As an aside, this isn't the only way to do things.  SmallTalk allows 
any message to be sent to any object, with a default method of "that
object doesn't know how to do that."  The philosophy is that you
don't want to implement a method until you need it.  The C++ (and
Object C) philosophy is that you should plan ahead.  I won't defend
the rules further, except to state my opinion that C++ has the right
rules for production software: my customer doesn't know what to do with
"*gApplication doesn't know how to IEditApp."

Notice that in practice, you almost never need to actually write the
casts.  Initialization is the most common case, in fact, and if you
want clean, no cast code, try:

	CMyAppType *myApp;
	...
	myApp->IMyAppType(...);
	...
	gApplication = myApp;	/* clean if CMyAppType inherits from 
				 * CApplication.
				 */

Hope that helps...

=Ned Horvath=

olson@bootsie.UUCP (Eric Olson) (12/07/90)

In article <3028@skye.cs.ed.ac.uk> nick@lfcs.ed.ac.uk writes:
>
>As it should. I'm not sure what the argument to new() actually is/does, but
>my guess is that it just provides a size (a la "sizeof") to determine how
>much store to claim.
>
>gApplication is still a CApplication, and hence doesn't have an IEditApp()
>method unless you cast it.
>
[The next line is not writen by nick -EKO]
>> new is meant to "automatically assign a class to the newly created object"
>
>Now, I've never understood what this kind of thing means, ditto for Bless()
>and all the rest. Presumably it does something automagical to the method
>dispatcher. Anybody care to comment what "the class of an object" actually
>means (as opposed to the static type of the object pointer)?
>

The object class types (like "CBorder") are a5-relative addresses, stored
at the first longword of the Handle allocated by new().  a5 is subtracted
off before the class type is stored by bless() or new(), and added in
in the function below (so that comparisons will work right).

You have to coerce the value returned by GetObjectClass() to (void *) before
passing it to some other functions.  As far as I know, functions can not
return (void *) .

/*=============================================================================
 GetObjectClass

            Get the type of an object.
            member(object, GetObjectClass(object)) always returns nonzero.
 =============================================================================*/

long GetObjectClass(
       CObject *               obj)
{
       asm {
         movea.l obj,a1
         movea.l (a1),a1                         ;  <<indirect>>
         move.w  (a1),d0                         ;  D0.W = class ID
         ext.l   d0                              ;  Sign extend the class ID
         add.l   a5,d0                           ;  Add in a5
  }
}

Cheers!

-Eric

-- 
Eric K. Olson, Editor, Prepare()       NOTE: olson@bootsie.uucp will not work!
Lexington Software Design              Internet: olson@endor.harvard.edu
72A Lowell St., Lexington, MA 02173    Usenet:   harvard!endor!olson
(617) 863-9624                         Bitnet:   OLSON@HARVARD

olson@bootsie.UUCP (Eric Olson) (12/07/90)

In article <40@bootsie.UUCP>, I wrote:

>The object class types (like "CBorder") are a5-relative addresses, stored
>at the first longword of the Handle allocated by new().  a5 is subtracted
              ^^^^^^^^

Oops.  That should say "word" or "short", not "longword".  The class types
themselves are longs, since they have a5 added in.

-Eric

-- 
Eric K. Olson, Editor, Prepare()       NOTE: olson@bootsie.uucp will not work!
Lexington Software Design              Internet: olson@endor.harvard.edu
72A Lowell St., Lexington, MA 02173    Usenet:   harvard!endor!olson
(617) 863-9624                         Bitnet:   OLSON@HARVARD

Invader@cup.portal.com (Michael K Donegan) (12/08/90)

This is the major difference between an interpreted language like
Smalltalk and a compiled one like C++ or Think C.  In Smalltalk,
you can send any message to any object.  If that object does not
understand the message, an error occurs.

This lack of type checking is inconvenient, but can also have
some nice results.  In Smalltalk, the notion of type is much
more flexible: in a given context, an object is the right type
if it handles the messages sent to it and satisfies the semantics
expected of those messages.

Given this notion of type, you can make an object masquerade as another
object by supplying the right methods.  This can't be done in Think C
unless both classes have a common base class that has the same methods
and we have declared our object to be of the common base class.

So, for example, in Smalltalk, there are Stream's that can be read
from using methods like 'next' to fetch the next thing in the stream
and 'atEnd' to test if the stream is exhausted.  I could create a
new class and use it in some context as a Stream, if I supply the
two methods.  My new class does not have to be a subclass of
Stream.

But then, Smalltalk is a lot slower than C++ or Think C, so we
just have to pay the price of this loss of generality.  It would
be nice if we could have both, though.
	mkd

emmayche@dhw68k.cts.com (Mark Hartman) (12/11/90)

In article <PHILS.90Dec6094112@chaos.cs.brandeis.edu>,
phils@chaos.cs.brandeis.edu (Phil Shapiro) writes:
 
>The only part of an object (in ThC) that indicates its class type is
>the "Class ID".  This is a word-sized number assigned to each class at
>compile time.  When you call a method, the method dispatcher, __msg(),
>uses this number to determine which "method table" to use.
 
Understanding that these "Class ID" numbers will vary from application to
application (and even from compile to compile of the same application),
they would still come in very handy for a "save state" kind of use.
 
How is it possible to access these Class ID numbers and relate them to
a class so that I can read an instance back in from a file and Bless() it
to the proper class?
 
Thanks

-- 
Mark Hartman, N6BMO           "What are you just standing there for?  Where
Applelink: N1083 or BINARY.TREE      do you think you are, DIS-ney World??"
Internet: emmayche@dhw68k.cts.com                -- General Knowledge, from
uucp: ...{spsd,zardoz,felix}!dhw68k!emmayche                CRANIUM COMMAND

phils@chaos.cs.brandeis.edu (Phil Shapiro) (12/11/90)

In article <1990Dec10.174536.17002@dhw68k.cts.com> emmayche@dhw68k.cts.com (Mark Hartman) writes:
   [ ... I said that objects are uniquely identified at compile time
     by "Class ID"s ... ]

   Understanding that these "Class ID" numbers will vary from
   application to application (and even from compile to compile of the
   same application), they would still come in very handy for a "save
   state" kind of use.

   How is it possible to access these Class ID numbers and relate them
   to a class so that I can read an instance back in from a file and
   Bless() it to the proper class?

The easy answer is to use the two classes that have been written
already to do this.  There is one for Pascal called "Instance.p" by
Bill Stackhouse, and a C version (based on Bill Stackhouse's code)
called "CInstance.c" by Sven Axelsson.  The C version is part of a
larger package from Sven Axelsson that has many examples of Think C
code.  You should be able to find these files at your usual ftp
archive.

The hard answer is that the Class ID is a 2 byte integer value stored
at the beginning of the object.  So, if you have an indirect object
"o", you can use "*(short *)(*o)" in C.  At run time, you can use this
information to relate a object to its class, that's how member()
works.  Of course, the location and size of the Class ID may change in
future versions of the Think compilers.

	-phil
--
   Phil Shapiro                           Technical Support Analyst
   Language Products Group                     Symantec Corporation
		Internet: phils@chaos.cs.brandeis.edu
-- 
   Phil Shapiro                           Technical Support Analyst
   Language Products Group                     Symantec Corporation