From: jonh@pogo.wv.tek.com (Jon Howell)
Newsgroups: comp.sys.mac.programmer
Subject: Re: Think C Debugger & Class dynamic typing... summary
Date: 14 Aug 92 17:16:38 GMT
Followup-To: comp.sys.mac.programmer
Organization: Tektronix

[summary]
In article <jonh-130892150657@jonh.wv.tek.com>, jonh@pogo (that's me!)
asked:
> When you look
> at a CObject in the Data window of the debugger, and dereference it to open
> up a view of the object's structure, it views the object as though it were
> the type it's declared as in the context. Why can't/doesn't it use the type
> the object *really* is?  For example:
> 
> {
>    CView *hokey;
> 
>    hokey = new CWindow;
>    hokey ->IWindow(blah blah blah);
> <> hokey ->Update();
> }
[I forgot to cast hokey above to make the method lookups work. I need a
 newsreader with a built-in precompiler. :v]
I also knew I could examine hokey in the debugger as a CWindow with:
       *(CWindow *) hokey
However, I really wish the debugger could do that on its own.
Per Mildner <Per.Mildner@csd.uu.se> points out that

PM>There is a macro you can use in the debugger that tells you the class
PM>name of an object as a string. I don't remember the name but it starts
PM>with two (or possibly one) underscore. It's in the documentation
PM>somewhere.

This would come in handy when you don't know just what hokey is at the
moment, so you could look it up to do the cast.

> Another question: TC provides member() and class_name() (and another I
> forget) functions for figuring out the dynamic type of an object. I'd
> like to be able to see if an object I've been passed is of a certain
> type. However, I don't want to do a string compare (class_name), and
> member() seems to only tell me if the object is a member of class X or
> one of its ancestors.  Is there a way I can get an integer which
> uniquely identifies the class? (It'd be extra cool if it gave some info on
> the hierarchy, but... :v)
To which markw@wc.novell.COM (Mark Wittenberg) responded to in the best
way -- with code! I'll include his message with a minimum of butchering
here. Thanks to you two and d88-jwa@nada.kth.se for the replies.

----- snip here -----
OK, there is a way.  Here are class.h and class.c that do that and more.
GetClassID does what you want, and there are several other very useful
routines as well.  You can just use Think's "__class" call directly if
that's really all that you want (I indicate "not a class" with an id
of -1 rather than Think's 0).

Note that MapClassNameToID is code largely ripped from oops.c, and this
code uses unsupported and undocumented features of v5.0.2 (translation:
I carefully studied oops.h and dug through memory a lot); I've been using
it a long and it's been very solid for me, but caveat emptor.

NewObjectByClassId is particularly nice because it's noticeably faster
than new_by_name.

/markw

---------------- class.h ----------------------
/*
 * Class.h
 *      - Class ID declarations
 *
 * Copyright 1991 Mark Wittenberg
 */

#pragma once

#include <CObject.h>

#define kNilClass       (-1)

typedef Str255  MAName;
typedef int             ObjClassID;

ObjClassID      GetClassID(CObject *theObject);
void            GetClassName(CObject *obj, MAName theName);
void            GetClassNameFromID(ObjClassID id, MAName theName);
ObjClassID      GetClassIDFromName(const MAName theName);
ObjClassID      MapClassNameToID(const MAName theName);
void            *NewObjectByClassId(ObjClassID currentID);

---------------- class.c ----------------------
/*
 * Class.c
 *      - Class ID routines
 *
 * Copyright 1991 Mark Wittenberg
 */

#define OOPS_PRIVATE
#include "Oops.h"               // change case so that <TCL Headers>
doesn't mess us up
#define INDIRECT
#ifdef INDIRECT
        #define __new __new_indirect
#else
        #define __new __new_direct
#endif

extern void *__new(...);

#if __option(far_code) || __option(far_data)
        ERROR - We assume 2-byte IDs!
#endif

#include "Class.h"

/*
 * GetClassID
 *      - Return the class id of an object.
 */
ObjClassID
GetClassID(CObject *obj)
{
        ObjClassID      id;
        
        id = (ObjClassID) __class(obj);
        return (id == 0) ? kNilClass : id;
} /* GetClassID */

/*
 * GetClassName
 */
void
GetClassName(CObject *obj, MAName theName)
{
        unsigned char   *name;
        
        theName[0] = '\0';
        name = (unsigned char *) class_name(obj);
        if( name )
                BlockMove( name, theName, name[0] + 1 );
} /* GetClassName */

/*
 * GetClassNameFromID
 */
void
GetClassNameFromID(ObjClassID id, MAName theName)
{
        unsigned char   *name;
                
        theName[0] = '\0';
        name = (unsigned char *) __class_name(id);
        if( name )
                BlockMove( name, theName, name[0] + 1 );
} /* GetClassNameFromID */

/*
 * GetClassIDFromName
 */
ObjClassID
GetClassIDFromName(const MAName theName)
{
        ObjClassID      id;
        
        id = MapClassNameToID( theName );
        return (id == 0) ? kNilClass : id;
} /* GetClassIDFromName */

/*
 * MapClassNameToID
 */
ObjClassID
MapClassNameToID(const MAName theName)
{
        struct { void *min, *max; } range;
        register void *p, *q;

        asm {

;;
;
;  establish search range
;
;;

#if __option(a4_globals)
                movea.l a4,a0
                _RecoverHandle
                _GetHandleSize
                move.l  a4,range.min
                add.l   a4,d0
                move.l  d0,range.max
#else
                move.l  CurStackBase,range.min
                move.l  a5,range.max
#endif

;;
;
;  search data area for matching name (this could probably be improved)
;
;;

                movea.l range.min,p
                movea.l theName,q
                move.w  (q)+,d1                                 ;  D1.W =
1st two bytes of name
@1              cmp.w   (p)+,d1
                beq.s   @2
                cmpa.l  range.max,p
                blo.s   @1
;
                moveq   #0,d0                                   ;  search
failed
                return
;
@2              movea.l p,a0
                movea.l q,a1
                bra.s   @4
@3              cmpm.b  (a0)+,(a1)+
                bne.s   @1                                              ; 
name not found - keep looking
@4              tst.b   -1(a1)
                bne.s   @3

;;
;
;  verify we've found a class info record
;
;;

                moveq   #~1,d0
                and.w   -4(p),d0
                lea             -4(p,d0.w),a0                   ;  A0 ==>
dispatch table
                cmpa.l  range.min,a0
                blo.s   @1                                              ; 
out of range - try again
                cmpa.l  range.max,a0
                bhs.s   @1                                              ; 
out of range - try again
;
                moveq   #1,d0
                add.w   (a0),d0
                lsl.w   #DSHIFT,d0
                lea             ClassInfo_(name[2])+2(a0,d0.w),a1
                cmpa.l  p,a1
                bne.s   @1                                              ; 
sanity check failed - try again

;;
;
; now a0 points to the dispatch table
;
;;
                move.l  a0,d0
        #ifdef BASE_REG
                sub.l   BASE_REG,d0
        #endif
                return
        }
} /* MapClassNameToID */

/*
 * NewObjectByClassId
 */
void *
NewObjectByClassId(ObjClassID id)
{
        void    *cptr;
        
        asm {
                move.w  id,d0
                ext.l   d0
        #ifdef BASE_REG
                add.l   BASE_REG,d0
        #endif
                move.l  d0,cptr
        }
        return __new( cptr );
} /* NewObjectByClassId */
-- 

-----------------------------------------------------------------------------
jonh@pogo.wv.tek.com     Jon Howell