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