[comp.std.c++] type/member tags

craig@gpu.utcs.utoronto.ca (Craig Hubley) (02/20/91)

In article <1991Feb19.191731.4137@pa.dec.com> lattanzi@decwrl.dec.com (Len Lattanzi) writes:
>In article <65451@brunix.UUCP> sdm@cs.brown.edu (Scott Meyers) writes:
>:In article <607@taumet.com> steve@taumet.com (Stephen Clamage) writes:
>:| >imagine a database query:
>:| 
>:| >	"from the list of shapes, show me all the circles of diameter > 30"
>:| 
>:| All you have to do is make a virtual function diameter() for all Shapes.

Yes, the existing C++ mechanisms provide a way to do this.  But there was
also a way to do templates with macros, which was prone to errors and abuse.
So templates were blessed.  I would suggest the same thing is true with this.

>:| A function which expects to operate on a class of type Shape may
>:| reaonably expect all the functionality of Shape to be present.  It may
>:| not reasonably expect specific behaviors of classes derived from Shape
>:| to be present.
>:
>:This is of course the right way to go, and most of the time it is a
>:workable solution, but there are times when it simply won't do.

Unfortunately many of us seem, including Bjarne, seem to be stuck in 
a mold of believing a general theory, like strong typing or object-
oriented hierarchies, and acknowledging exceptions to it instead of
reconsidering the theory.  So far as I am concerned, strong typing
means little in a language with conversion, and hierarchy is not the
only blessed way to share specifications or code, as templates prove.

>:I personally believe that we need language support for this kind of thing,
>:but it would be so prone to abuse and Bjarne is so dead-set against it that
>:I don't hold out much hope that it will be seriously considered by the ANSI

There are ways to cut the abuse.  Consider these possible solutions:

PROBLEM	accessing features that do not exist on all of the possible types of
	a passed object, e.g. "is this a circle of diameter > 30 ?"

TODAY	explicit type tag
	- inconsistent across user programs
	- cannot be added to imported classes

SOL.1	type switch, similar to value switch
	- functions specific to more specialized class available in case-arm
	- class name used to distinguish type of object

	class_switch x {
		case circle:	
			if (x.diameter > 30) then {
				...
			}
	}

SOL.2	.class member, similar to .this member
	- public data member class, read-only
	- class name usable only in equivalence expression
	- equivalence rules for derived types
	
	if (x.class == circle) && ((circle)x.diameter > 30)) then {
		...
	}

SOL.3	conditional cast, similar to catch
	- if cast is illegal, no action, entire expression returns false
	- if cast is legal, it occurs and rest of block is executed
	- requires new syntax (here represented as double parentheses)
	
	if ( ((circle)) x.diameter > 30) then {
		...
	}

SOL.4	as above, only testing for presence of members, not name of classes
	- avoids dependencies on current class/member structure
	- works for inherited members

The above would require anyone who really intended to abuse the information
to continue to keep his/her own type tags, so this abuse would not be
"blessed".  In fact, I think opportunities for abuse would markedly diminish
if one of the above mechanisms was added.  After all, 95% of your need for
a type tag would be covered, and why would you invent your own mechanism
for that abusive 5% ?  However, if you have to invent your own anyway, or
use a base class library that has its own, you are more likely to take
advantage of it and increase dependencies in your code.  At least IMHO.

>:committee.  Instead, I think that compiler vendors will add proprietary
>:support for it, and there will be a market free-for-all.  I know that at

This is to be avoided at all costs.  Arg, a grass-roots rebellion against
the language definition... ye gods.  Look what this did to COBOL and Pascal.

>:least one compiler vendor already has implemented such a facility for
>:getting type information at runtime.

I hope whatever they do is supportable as a standard, because you can bet
it'll end up as one.

>:The bottom line is that virtual functions are wonderful things and can
>:almost always be used to achieve what you want, but there are times when
>:you truly *do* need to get type information as you run.

I agree, and so do designers of all other o-o languages.  It just isn't
a controversy, except for Bjarne it seems.  At least I haven't seen any
evidence of someone else saying you should never see the type of an object.
I hope ANSI C++ doesn't end up as a half-baked collision of object-oriented
dogma and C dogma.  The mere fact that compiler developers are including it
is a strong sign of user demand, and it is obviously not ignorant demand.

>:Scott
>
>This is especially true for expanding library interfaces. I wished for

Frankly I think Bjarne's concern is entirely misplaced.  I would worry far
more about the dozens of weird mechanisms for getting at type information than
about non-robust uses of it, or about the possibility that someone will
invent their own style of virtual functions given that information.
Although I recently proposed a different kind of virtual function with
different overriding rules, I would rather work within, or change, the language
than invent such a scheme for use only in my own programs.  However, I can't
be stopped from doing it, even by hiding the type tag.  So what is gained by
hiding it, I don't know.

>an "interactive(istream&)" predicate but had my hands tied. I could
>either compare pointer-to-members to guess at class type (boo! hiss!)
>or use iostream state variables to record this information. Something
>like a property-list per object is probably the desire of most lisp hackers.

Speaking as an ex-Lisp hacker, right on.  But this needn't wait until runtime
to be resolved, and compromise C++'s precious efficiency.  There are such
things as optimizing compilers, which C-heads usually have never heard of
because the language's low-level constructs are so pervasive and hard to 
optimize.  I've heard C described as "welfare for mediocre compiler writers",
and after using C for 7 years I tend to agree.

> Len Lattanzi (Migration Software Systems Ltd 408 452 0527) <len@migration.com>

The next thing I expect compiler writers to do is hack the linker so that
new versions of compiled-in libraries can be patched in at runtime.  They
gotta go into the object code, hack out calls to the subroutines that changed
and replace 'em with a call to a jump table into the new library.  Compatible
with C ?  Hah ha hahahah... but there are definitely a lot of people, even on
the ANSI committee, that couldn't care less about C.  And I agree with 'em.
Who actually compiles C sources under C++ anyway ?
-- 
  Craig Hubley   "...get rid of a man as soon as he thinks himself an expert."
  Craig Hubley & Associates------------------------------------Henry Ford Sr.
  craig@gpu.utcs.Utoronto.CA   UUNET!utai!utgpu!craig   craig@utorgpu.BITNET
  craig@gpu.utcs.toronto.EDU   {allegra,bnr-vpa,decvax}!utcsri!utgpu!craig

chip@tct.uucp (Chip Salzenberg) (02/21/91)

According to craig@gpu.utcs.utoronto.ca (Craig Hubley):
>PROBLEM accessing features that do not exist on all of the possible types of
>	a passed object, e.g. "is this a circle of diameter > 30 ?"
>
>TODAY	explicit type tag
>	- inconsistent across user programs
>	- cannot be added to imported classes

Bzzt.  You forgot:

TODAY.2 virtual member function that does the operation or reports
        failure and/or raises an exception.
	- uses language features already available
	- doesn't require client changes when class hierarchy changes

>SOL.2	.class member, similar to .this member

This solution would require a unique program-wide integer that would
represent each class.  (Can you say Objective-C?  Ugh.)  It would also
impose the space overhead of virtual functions on classes that do not
need it.  It would further make programs dependendent on the details
of inheritance.  ("Let's see, I want all subclasses of foo except bar,
um, they're larry, moe, curly..."  Double ugh.)

>SOL.3	conditional cast, similar to catch

Eliminates the knowledge-of-inheritance problem, but cost-of-virtual
and unique-class-id problems are still there.

>SOL.4	as above, only testing for presence of members, not name of classes

How do you know that "circle.size()" and "building.size()" are in any
way related to each other?  You don't.  Member name is meaningless
without some indication of which "size()" is the Right One.  So once
you figure out which one is the Right One, make it virtual.  Presto.

>It just isn't a controversy, except for Bjarne it seems.  At least I
>haven't seen any evidence of someone else saying you should never see
>the type of an object.

Count me in with Bjarne.  You, the programmer, never really want to
know the type.  You may think you want to know the type, but why?  So
you can know if a feature is available.  So make the feature a virtual
function and be done with it.

>I hope ANSI C++ doesn't end up as a half-baked collision of object-oriented
>dogma and C dogma.

Why change now?  :-)

>The mere fact that compiler developers are including it is a strong
>sign of user demand, and it is obviously not ignorant demand.

"Obviously?"  It's not obvious to me.

>There are such things as optimizing compilers, which C-heads usually have
>never heard of because the language's low-level constructs are so pervasive
>and hard to optimize.

I know all about optimizers, thank you very much.  They are utterly
irrelevant to the issues of type knowledge.
-- 
Chip Salzenberg at Teltronics/TCT      <chip@tct.uucp>, <uunet!pdn!tct!chip>
"It's not a security hole, it's a SECURITY ABYSS." -- Christoph Splittgerber
   (with reference to the upage bug in Interactive UNIX and Everex ESIX)

dsouza@optima.cad.mcc.com (Desmond Dsouza) (02/25/91)

This topic does come up quite often, doesn't it?

In article <27C2D973.3C1B@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:

	>   Bzzt.  You forgot:
	>
	>   TODAY.2 virtual member function that does the operation or reports
	>	   failure and/or raises an exception.
	>	   - uses language features already available
	>	   - doesn't require client changes when class hierarchy changes
	>
	>   >SOL.2	.class member, similar to .this member
	>	if (x.class == circle) && ((circle)x.diameter > 30)) then {
	>
	>   This solution would require a unique program-wide integer that would
	>   represent each class.  (Can you say Objective-C?  Ugh.)  It would also
	>   impose the space overhead of virtual functions on classes that do not
	>   need it.  It would further make programs dependendent on the details
	>   of inheritance.  ("Let's see, I want all subclasses of foo except bar,
	>   um, they're larry, moe, curly..."  Double ugh.)
	>

This only requires that (x.class) be of type, say, "ClassId", which
supports "ClassId == ClassId" for class identity, and possibly
"ClassId <= ClassId" for sub-class checks. The user does not need to
worry about details of inheritance as this is done by the ClassId
type.  ClassId could also contain lots of other incredibly useful
information in general.

ClassId may be implemented as a string encoding the object's class
name as well as the class names of all its ancestors, or as one of
many possible alternate encodings of hierarchies. It does not require
a unique program-wide integer per class.

It is reasonable for x.class to be a virtual function call ONLY if
class X already has virtual functions (why else would you have a
Circle in a list of Shapes?). Otherwise, it could be just a regular
member function call. Hence, you do not have to add virtual-ness to 
otherwise non-virtual classes.

	>
	>   >It just isn't a controversy, except for Bjarne it seems.  At least I
	>   >haven't seen any evidence of someone else saying you should never see
	>   >the type of an object.
	>
	>   Count me in with Bjarne.  You, the programmer, never really want to
	>   know the type.  You may think you want to know the type, but why?  So
	>   you can know if a feature is available.  So make the feature a virtual
	>   function and be done with it.


There are a lot of cases where free access to type information would
be better replaced with virtual functions, BUT NOT ALL.
Here are a few examples where you need to know the type of an object:

1. Persistent objects: When reading in one of these from disk, you
   need to know what constructor to call. Hence you need to encode in
   the persistent image the ClassId of the object.
2. You have an interactive system where a mouse-weilding user
   dynamically configures objects together. You need to check the
   configuration for validity, which needs many of the same
   type-relationship checks that the compiler would perform, were it
   statically analyzing program text which configured the same objects.
3. Almost any application related to a programming environment:
   debuggers, smart re-compilers, ...

	>   -- 
	>   Chip Salzenberg at Teltronics/TCT      <chip@tct.uucp>, <uunet!pdn!tct!chip>
	>   "It's not a security hole, it's a SECURITY ABYSS." -- Christoph Splittgerber
	>      (with reference to the upage bug in Interactive UNIX and Everex ESIX)


The compiler has access to almost all of this information at compile
time. If the above 3 examples reflect a legitimate need, it seems
unreasonable for the user to worry about them all over again. Also,
C++ cast restrictions on virtual-base classes (cannot cast from
virtual base to derived) could be removed if the compiler made some of
this information available at run-time!


Desmond D'Souza
MCC.
--

-------------------------------------------------------------------------------
 Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com | Phone: [512] 338-3324
 Box 200195, Austin, TX 78720 | UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!dsouza

chip@tct.uucp (Chip Salzenberg) (02/26/91)

According to dsouza@optima.cad.mcc.com (Desmond Dsouza):
>... only requires that (x.class) be of type, say, "ClassId", which
>supports "ClassId == ClassId" for class identity, and possibly
>"ClassId <= ClassId" for sub-class checks. ... It does not require
>a unique program-wide integer per class.

I stand corrected.

>It is reasonable for x.class to be a virtual function call ONLY if
>class X already has virtual functions ... Hence, you do not have to
>add virtual-ness to otherwise non-virtual classes.

I stand corrected^2.  It wouldn't be the efficiency hog I had claimed.

Nevertheless, I have a hard time supporting the feature.  Every time I
hear "typeof(foo)" I see masses of poorly-written Smalltalk: "foo
isTypeOf: OrderedCollection or: foo isTypeOf: UnorderedCollection..."

>Here are a few examples where you need to know the type of an object:

"Need?"  That's a red flag for me... :-)

>1. Persistent objects: When reading in one of these from disk, you
>   need to know what constructor to call. Hence you need to encode in
>   the persistent image the ClassId of the object.

Presumably, a well-designed class hierarchy will use a virtual
function to store objects; and a virtual function by definition will
already know the exact type of the object it is storing.

>2. You have an interactive system where a mouse-weilding user
>   dynamically configures objects together. You need to check the
>   configuration for validity ...

ClassID would be a useful tool in such code, but it is by no means
necessary.  For example, each class can have virtual functions along
the lines of "int can_be_owned()".  And do you want J. Random Coder
making decisions like "if x is a Widget, but not a FooWidget..."

>3. Almost any application related to a programming environment:
>   debuggers, smart re-compilers, ...

I can see it for debugging.  But the extra cost of the feature is not
something I'd want to inherit permanently in my delivered executable.
And I must say that debugging alone is a little thin as justification
for a language feature with run-time costs.

>C++ cast restrictions on virtual-base classes (cannot cast from
>virtual base to derived) could be removed if the compiler made some
>of this information available at run-time!

But you can always write a virtual function that does the cast.
-- 
Chip Salzenberg at Teltronics/TCT      <chip@tct.uucp>, <uunet!pdn!tct!chip>
"It's not a security hole, it's a SECURITY ABYSS." -- Christoph Splittgerber
   (with reference to the upage bug in Interactive UNIX and Everex ESIX)

jimad@microsoft.UUCP (Jim ADCOCK) (02/26/91)

In article <DSOUZA.91Feb24173454@optima.cad.mcc.com> dsouza@optima.cad.mcc.com (Desmond Dsouza) writes:
|
|This topic does come up quite often, doesn't it?

Yep.  Seems like the C++ world is solidly in two camps on this issue.
One camp says any notion of run-time type is bad, and we're not going to
support it.

The other camp says that in some situations some notions of run-time type
are useful, and C++ ought to have some good way to support it.

My claim is that if some notions of run-time type are not in the language,
then a lot of C++ vendors are simply going to go out and implement their
own, incompatible notions of run-time type.

I suggest that run-time type info can be specified as part of the language,
can be done in a way that classes that don't want to use such a feature
pay no price, and that run-time type info can be implemented in compilers
at fairly low cost.

Therefore I suggest that compile vendors ought to agree to get together and
decide how this should be done.  Leave it to the end programmer whether they
want to use the feature or not, just like today we leave it to the end
user whether they decide to use multiple inheritence or not.

I further claim that problems with run-time type demonstrate a general
problem in c++:  today there is no good way to specify features in a base
class that are to be implemented in each derived class.  You'd think there'd
be some way to use templates for such problems, but today that doesn't
seem true.  The best one can do today appears to be the macro class
declaration/definition hack:

DECLARE_CLASS(derived, base)
....
};

DEFINE_CLASS(derived, base)


And, like always, I claim any common, recurring theme that programmers
consistently have to address via the preprocessor represents a deficiency
in the language.

wmm@world.std.com (William M Miller) (02/26/91)

jimad@microsoft.UUCP (Jim ADCOCK) writes:
> I suggest that run-time type info can be specified as part of the language,
> can be done in a way that classes that don't want to use such a feature
> pay no price, and that run-time type info can be implemented in compilers
> at fairly low cost.

We agree.  In fact, I submitted an abstract for a paper for the April USENIX
C++ Conference describing exactly such a design; however, the program
committee decided that a design-only paper without implementation and user
experience did not meet their acceptance criteria.

> Therefore I suggest that compile vendors ought to agree to get together and
> decide how this should be done.

That was the reason for submitting the paper for publication in the first
place.  We're still interested in discussions with other compiler vendors to
see if a de facto standard for runtime type information can be established.
Perhaps we can get some of the interested parties together at the upcoming
X3J16 meeting in Nashua.

-- William M. Miller, Glockenspiel, Ltd.
   wmm@world.std.com

dsouza@optima.cad.mcc.com (Desmond Dsouza) (02/27/91)

In article <27C95D3A.1715@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:

	>   >Here are a few examples where you need to know the type of an object:
	>
	>   "Need?"  That's a red flag for me... :-)

It is a red flag for me too. And lots of red flags mean it is quite
possibly a legitimate shortcoming.

	>   >1. Persistent objects: When reading in one of these from disk, you
	>   >   need to know what constructor to call. Hence you need to encode in
								 ^^^^^^^^^^^^^^
	>   >   the persistent image the ClassId of the object.
	>
	>   Presumably, a well-designed class hierarchy will use a virtual
	>   function to store objects; and a virtual function by definition will
	>   already know the exact type of the object it is storing.

Yes, but it needs to make it _explicitly_ available in the binary image.

Retrieving and re-constructing persistent objects is where you *need*
explicit type information.  Virtual functions dont help now because
you do not have an existing object to dispatch off.  Storing objects
is relatively easy, e.g. with a virtual function which writes out some
ClassId information. Hence, your objects must have a way to access
(and write out) their true type.

Ditto for transferring objects across address spaces, networks, etc.

	>   >2. You have an interactive system where a mouse-wielding user
	>   >   dynamically configures objects together. You need to check the
	>   >   configuration for validity ...
	>
	>   ClassID would be a useful tool in such code, but it is by no means
	>   necessary.  For example, each class can have virtual functions along
	>   the lines of "int can_be_owned()".  And do you want J. Random Coder
	>   making decisions like "if x is a Widget, but not a FooWidget..."

Solutions like "virtual int can_be_owned()" are an incredible
implementation overhead when you are dealing with combinations of
object classes and relationships between them. 

e.g. Can an A be plugged into a B in the slot x ? How about an A1 into
slot z of a B1?

Why not offer the type checking facility and teach J. Random Coder
when _not_ to use it ?

	>   >3. Almost any application related to a programming environment:
	>   >   debuggers, smart re-compilers, ...
	>
	>   I can see it for debugging.  But the extra cost of the feature is not
	>   something I'd want to inherit permanently in my delivered executable.
	>   And I must say that debugging alone is a little thin as justification
	>   for a language feature with run-time costs.

The cost, as you admitted, can be quite manageable. The primary
problem, as Stroustrup points out, is pick an acceptable amount of
functionality for 'ClassId'.

Check out some papers on "reflection" in languages to see just how
powerful classes-as-objects _can_ be. In fact, persistence, dynamic
configuration and debugging can be all viewed as "reflective"
capabilities. 

Consider the proposed (now accepted) Exception Handling scheme for
C++. The statement:
  catch (ErrorClass& o) {
	....
  }
is supposed to 'catch' a deeply nested call to:
  throw ErrorClass("someError");

Any implementation of this involves a run-time type check, including
class derivation, on the object being thrown. Can you say 'Meta-class'?

Most static class variables and static class functions (explicit or
implicit) can be considered members of a corresponding class object.

	>   >C++ cast restrictions on virtual-base classes (cannot cast from
	>   >virtual base to derived) could be removed if the compiler made some
	>   >of this information available at run-time!
	>
	>   But you can always write a virtual function that does the cast.

True. It is painful, error-prone, and the compiler can do a much better job.

	>   Chip Salzenberg at Teltronics/TCT      <chip@tct.uucp>, <uunet!pdn!tct!chip>

Desmond.
--

-------------------------------------------------------------------------------
 Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com | Phone: [512] 338-3324
 Box 200195, Austin, TX 78720 | UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!dsouza

glenn@huxley.huxley.bitstream.com (Glenn P. Parker) (02/27/91)

In article <27C95D3A.1715@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
> According to dsouza@optima.cad.mcc.com (Desmond Dsouza):
>>1. Persistent objects: When reading in one of these from disk, you
>>   need to know what constructor to call. Hence you need to encode in
>>   the persistent image the ClassId of the object.
> 
> Presumably, a well-designed class hierarchy will use a virtual
> function to store objects; and a virtual function by definition will
> already know the exact type of the object it is storing.

Check that article again.  Desmond is talking about reading in a persistent
object, not writing it out.  I believe this is the most common (and most
easily explained) situation that requires "extra" type information.

--
Glenn P. Parker       glenn@bitstream.com       Bitstream, Inc.
                      uunet!huxley!glenn        215 First Street
                      BIX: parker               Cambridge, MA 02142-1270

peterson@choctaw.csc.ti.com (Bob Peterson) (02/27/91)

In article <27C95D3A.1715@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>According to dsouza@optima.cad.mcc.com (Desmond Dsouza):
>...
>>1. Persistent objects: When reading in one of these from disk, you
>>   need to know what constructor to call. Hence you need to encode in
>>   the persistent image the ClassId of the object.
>
>Presumably, a well-designed class hierarchy will use a virtual
>function to store objects; and a virtual function by definition will
>already know the exact type of the object it is storing.
>

  This view of how to implement persistence seems narrow.  Researchers
and vendors have taken a number of implementation approaches, including
the one you mention.  The details of the various implementations vary
widely, but few depend on virtual functions to supply type information.
In all cases some indication of the actual type of the stored object
must be available when the object is recovered.  Of course, this
implies type information must be available when an object is to be
stored.  I daresay many of us working in the area of persistence would
welcome a standard way of encoding and accessing type information.

  Actually the problem generalizes quite readily to exporting and
importing an object from and to an address space, regardless of the
purpose.  An obvious purpose, other than storage, is for interprocess
communications.

>...
>-- 
>Chip Salzenberg at Teltronics/TCT      <chip@tct.uucp>, <uunet!pdn!tct!chip>
>"It's not a security hole, it's a SECURITY ABYSS." -- Christoph Splittgerber
>   (with reference to the upage bug in Interactive UNIX and Everex ESIX)
Bob Peterson                Compuserve: 70235,326          Expressway Site,
Texas Instruments           Internet: peterson@csc.ti.com   North Building,
P.O. Box 655474, MS238      Landline: (214) 995-6080         2nd Floor,
Dallas, Texas, USA 75265                                      CSC Aisle C3

chip@tct.uucp (Chip Salzenberg) (02/28/91)

According to dsouza@optima.cad.mcc.com (Desmond Dsouza):
>Retrieving and re-constructing persistent objects is where you *need*
>explicit type information.  Virtual functions dont help now ...

Of course they don't help; but explicit type information doesn't help,
either.  The language type system is inoperative when dealing with
objects that have been "frozen" into dead arrays of bytes.

>Storing objects is relatively easy, e.g. with a virtual function which
>writes out some ClassId information.  Hence, your objects must have a
>way to access (and write out) their true type.

When a virtual function is invoked, the type is implied, unless the
programmer has made the mistake of allowing a virtual function to be
inherited by a derived class that should be frozen in a distinct way
from its parent.

>Solutions like "virtual int can_be_owned()" are an incredible
>implementation overhead when you are dealing with combinations of
>object classes and relationships between them. 
>
>e.g. Can an A be plugged into a B in the slot x ? How about an A1 into
>slot z of a B1?

The problem as described is inherently complex.  The complexity of a
solution is neither created nor worsened by a solution of the
can_be_owned() type.

>Why not offer the type checking facility and teach J. Random Coder
>when _not_ to use it ?

I agree with this approach.  After all, if I opposed features purely
on the basis of possible misuse, then I wouldn't be using C++.  :-)

So in the end, my opposition isn't to the feature, but to the attitude
manifested by some that it's practically necessary for OOP.  It's not.

``isKindOf is the OOP equivalent of goto.''  --me

>Consider the proposed (now accepted) Exception Handling scheme for
>C++. The statement:
>  catch (ErrorClass& o) {
>	....
>  }
>is supposed to 'catch' a deeply nested call to:
>  throw ErrorClass("someError");

Unless I misunderstand the implementation issues, name equivalence can
solve this problem without resorting to dynamic type information, and
also without incurring run-time overhead that dynamic type checking
would likely impose.

> > But you can always write a virtual function that does the cast.
>
>True. It is painful, error-prone, and the compiler can do a much better job.

My experience is not so negative, though I grant that I'd prefer the
compiler to more of my work.  (Always!)
-- 
Chip Salzenberg at Teltronics/TCT      <chip@tct.uucp>, <uunet!pdn!tct!chip>
"It's not a security hole, it's a SECURITY ABYSS." -- Christoph Splittgerber
   (with reference to the upage bug in Interactive UNIX and Everex ESIX)

chip@tct.uucp (Chip Salzenberg) (02/28/91)

According to peterson@choctaw.csc.ti.com (Bob Peterson):
>In article <27C95D3A.1715@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>>Presumably, a well-designed class hierarchy will use a virtual
>>function to store objects ...
>
>Researchers and vendors have taken a number of implementation approaches
>...  The details of the various implementations vary widely, but few depend
>on virtual functions to supply type information.

Is there any reason to avoid virtual functions for object storage?

I had not envisioned a virtual function to return type information,
but rather a virtual function to actually _do_the_work_, so no one
outside of that function need know the actual type of the object
stored.
-- 
Chip Salzenberg at Teltronics/TCT      <chip@tct.uucp>, <uunet!pdn!tct!chip>
"It's not a security hole, it's a SECURITY ABYSS." -- Christoph Splittgerber
   (with reference to the upage bug in Interactive UNIX and Everex ESIX)

vaughan@puma.cad.mcc.com (Paul Vaughan) (03/01/91)

I work on an application that has objects that know their type
(implementation based on the NIHCL approach). This application also
has many classes which do not have type information. I see this as an
expression of the flexibility of C++--if you need type information,
you can program it, otherwise you're not burdened with its overhead.

However my attitude about this is not inflexible. I believe it would
make sense to include type related functions for classes which have
virtual functions as a standard part of the language. Overhead could
be restricted to a per class basis, rather than a per object basis.
Certain limitations, such as casting down from a virtual base class,
could then be overcome. Compatibility with C would not be affected.
Compatibility with existing code would be minimally affected. I
believe that the need for this sort of information on objects that
have virtual functions is pervasive enough to warrant this minimal
overhead. I believe that compiler supported implementations could be
substantially better than user programmed implementations. While I
don't have any good suggestions for how this might be supported
syntactically, I believe that a reasonable solution can be found.

In short, I believe that it makes sense to recognize "classes with
virtual functions" as an important concept worthy of a name and
special functionality.
--

 Paul Vaughan, MCC CAD Program | ARPA: vaughan@mcc.com | Phone: [512] 338-3639
 Box 200195, Austin, TX 78720  | UUCP: ...!cs.utexas.edu!milano!cadillac!vaughan
----------------------------------------------------------------------------------
I spent from $3 to $10 today to pay interest on the national debt.  How about you?
----------------------------------------------------------------------------------

Reid Ellis <rae@utcs.toronto.edu> (03/01/91)

Desmond Dsouza <dsouza@optima.cad.mcc.com> writes:
>Consider the proposed (now accepted) Exception Handling scheme for
>C++. The statement:
>  catch (ErrorClass& o) {
>	....
>  }
>is supposed to 'catch' a deeply nested call to:
>  throw ErrorClass("someError");
>
>Any implementation of this involves a run-time type check, including
>class derivation, on the object being thrown. Can you say 'Meta-class'?

This is simply overloading based on argument type.  Just like having
methods named "foo(Tbase &)" and "foo(Tderived &)".  No "Meta-class"
required.

					Reid
--
Reid Ellis  176 Brookbanks Drive, Toronto ON, M3A 2T5 Canada
rae@utcs.toronto.edu        ||  rae%alias@csri.toronto.edu
CDA0610@applelink.apple.com ||             +1 416 446 1644

chip@tct.uucp (Chip Salzenberg) (03/02/91)

According to vaughan@puma.cad.mcc.com (Paul Vaughan):
>In short, I believe that it makes sense to recognize "classes with
>virtual functions" as an important concept worthy of a name and
>special functionality.

I consider this a reasonable stand.  In particular, "classes with
virtual functions" should be identical to "classes with virtual
destructors."  I would go so far as to make illegal a class with
virtual functions but a non-virtual destructor.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>

jbuck@galileo.berkeley.edu (Joe Buck) (03/02/91)

In article <27CD159D.6581@tct.uucp>, chip@tct.uucp (Chip Salzenberg) writes:
|> I had not envisioned a virtual function to return type information,
|> but rather a virtual function to actually _do_the_work_, so no one
|> outside of that function need know the actual type of the object
|> stored.

You're still missing the point, Chip.  I'm generally against functions
to return type information, but there are places where you need to do
something of this type.  Sure, you can have a virtual function to
store an object on disk.  But how are you going to read it back?  There
is nothing on the disk but data.  A similar problem occurs when inter
process communication is required.  The system designer must provide
some scheme to allow the type to be determined.

The interesting thing, though, is that the solution many propose to
the problem of determining the type doesn't address this situation at
all!  If, say, I provide an automatically generated virtual function to
return the type somehow, that function won't tell me what's on the disk
either.

--
Joe Buck
jbuck@galileo.berkeley.edu	 {uunet,ucbvax}!galileo.berkeley.edu!jbuck	

rfg@NCD.COM (Ron Guilmette) (03/03/91)

I am cross-posing this response to comp.lang.c++ since it contains
information about a general problem that many C++ programmers (and
not just ones who are worried about the evolving standard) are concerned
about.

In article <27C95D3A.1715@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>According to dsouza@optima.cad.mcc.com (Desmond Dsouza):
>>Here are a few examples where you need to know the type of an object:
>
>"Need?"  That's a red flag for me... :-)
>
>>1. Persistent objects: When reading in one of these from disk, you
>>   need to know what constructor to call. Hence you need to encode in
>>   the persistent image the ClassId of the object.
>
>Presumably, a well-designed class hierarchy will use a virtual
>function to store objects; and a virtual function by definition will
>already know the exact type of the object it is storing.

As Joe Buck pointed out, storing (or transmitting) an object is *not*
a problem which requires any sort of special identification of the type
of an object, however retrieving (or receiving) an hunk of data which
represents the previous contents of some unknown type of thing requires
us to use some sort of agreed upon scheme whereby the transmitter sends
some unique code with the data block to indicate to the receiver what
type of C++ object the transmitted data came from.  Assuming that both
the transmitting program and the receiving program are written in C++,
and assuming that we want the receiving program to perform (or to fake)
the re-construction of the originally transmitted object, we need to
have some set of globally unique "type codes" (which the transmitter and
the receiver must agree upon).

We faced exactly this problem when I was working at MCC on the ES-kit
project.  We had a distributed multiprocessor system within which we
wanted to be able to migrate objects and to send "messages" (i.e.
member function calls) from one processing node to an object residing
on a different processing node.

The solution we came up with was not terribly elegant.  We ended up
hacking the compiler (g++) to get it to provide the actual string
representation of the class name to the OS kernel routine which was
responsible for forwarding messages between nodes.  The kernel then
had to do a lookup of the class name string within a table of all
class-name string known to the system in order to get a globally
unique 32-bit integer valued "code" for that particular class type.
This code was then shipped across the mesh as a part of the inter-node
"packet" representing the "message" to the remote object.

A much cleaner solution would have been to enlist the compiler & linker
to assign the globally unique "type codes" up front, prior to run-time.
This would have allowed us to avoid the (very expensive) table lookups
which we did within the kernel for each transmitted message.

Ada implementors are familiar with the problem of providing "globally
unique" identifying codes for things.  They face the same problem
when they go to implement Ada exceptions.  Various aspects of Ada
effectively create a requirement for an internal set of globally
unique identification "codes" for all of the Ada exceptions declared
throughout an Ada entire program.

In all Ada implementations I know of, these "globally unique" codes for
declared Ada exceptions are, in effect, generated at link-time by the
linker.  For each Ada exception declared within an entire Ada program
the Ada compiler generates one small (word sized) artificial variable.
When it subsequently needs a unique code for the given exception, it
simply uses the address of the associated "dummy" variable as the
globally unique "code" for the given exception.  Fortunately, the linker
see to it that all of these "dummy" variables get allocated to different
memory locations (during linking) so that the addresses of these
exceptions (i.e. their "codes") are indeed globally unique.

An identical scheme could be used to assign globally unique integer
codes to each class type within an entire (linked) C++ program.  For
each class type declaration compiled, a C++ compiler (or translator)
could generate a "dummy" variable with a particular (specially mangled)
name.  The address of that dummy variable could then be used as a
globally unique identifier for the class type itself.  For example,
given:

	class C {
		/...
	};

	void *vp;

	void example ()
	{
		vp = typeof (class C);
	}

A C++ translator could easily generate:

	struct C {
		//...
	};

	int __C__typeof_dummy;

	void *vp;

	void __exampleV ()
	{
		vp = (void *) &__C__typeof_dummy;
	}

This approach would work for C++ *translators* only so long as they are
connected to underlying C compilers which allocate uninitialized variables
(such as "__C__typeof_dummy") to "common".  ANSI C does not allow this
practice, but virtually all K&R C compilers and many "ANSI" C compilers
still do it anyway.

-- 

// Ron Guilmette  -  C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// New motto:  If it ain't broke, try using a bigger hammer.

craig@gpu.utcs.utoronto.ca (Craig Hubley) (03/03/91)

In article <27CD159D.6581@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>According to peterson@choctaw.csc.ti.com (Bob Peterson):
>>In article <27C95D3A.1715@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>>>Presumably, a well-designed class hierarchy will use a virtual
>>>function to store objects ...
>>
>>Researchers and vendors have taken a number of implementation approaches
>>...  The details of the various implementations vary widely, but few depend
>>on virtual functions to supply type information.
>
>Is there any reason to avoid virtual functions for object storage?

The need to derive all "storable" types from a given base.  Among other
problems, you can't store an int or a char* that way.  If you are using a
single base class library, you can define store() and retrieve() in that
base.

But this is not flexible enough for most OODBs.  A more common approach is
to define keywords like "persistent" that can be applied to any object. 
This requires preprocessing outside the definition of C++.  

>I had not envisioned a virtual function to return type information,
>but rather a virtual function to actually _do_the_work_, so no one
>outside of that function need know the actual type of the object
>stored.

This works so long as you are prepared to write a new function for each
storable type.  This is not what I expect from an OODB.


-- 
  Craig Hubley   "...get rid of a man as soon as he thinks himself an expert."
  Craig Hubley & Associates------------------------------------Henry Ford Sr.
  craig@gpu.utcs.Utoronto.CA   UUNET!utai!utgpu!craig   craig@utorgpu.BITNET
  craig@gpu.utcs.toronto.EDU   {allegra,bnr-vpa,decvax}!utcsri!utgpu!craig

chip@tct.uucp (Chip Salzenberg) (03/05/91)

According to craig@gpu.utcs.utoronto.ca (Craig Hubley):
>In article <27CD159D.6581@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>>Is there any reason to avoid virtual functions for object storage?
>
>The need to derive all "storable" types from a given base.  Among other
>problems, you can't store an int or a char* that way.

I wouldn't expect an OODB to be able to store a |char *|, since it has
no way to know what it's pointing at and/or who "owns" it.

>>I had not envisioned a virtual function to return type information,
>>but rather a virtual function to actually _do_the_work_, so no one
>>outside of that function need know the actual type of the object
>>stored.
>
>This works so long as you are prepared to write a new function for each
>storable type.  This is not what I expect from an OODB.

Surely you would in practice distinguish between "some entity needs to
write it" and "_I_ need to write it".  Any OODB worth the floppy it's
distributed on would generate such a function automatically.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
   "All this is conjecture of course, since I *only* post in the nude.
    Nothing comes between me and my t.b.  Nothing."   -- Bill Coderre

chip@tct.uucp (Chip Salzenberg) (03/05/91)

According to rfg@NCD.COM (Ron Guilmette):
>... retrieving (or receiving) an hunk of data which represents the
>previous contents of some unknown type of thing requires us to use
>some sort of agreed upon scheme whereby the transmitter sends some
>unique code with the data block to indicate to the receiver what
>type of C++ object the transmitted data came from.

In my mind, the only reasonable choice for such a unique code is the
class name itself.  The C++ language depends on name equivalence, so
user code may also depend on name equivalence without problems.

>The [messaging] kernel then had to do a lookup of the class name string
>within a table of all class-name string known to the system in order to
>get a globally unique 32-bit integer valued "code" for that particular
>class type.

Why not use the class name itself?

>A much cleaner solution would have been to enlist the compiler & linker
>to assign the globally unique "type codes" up front, prior to run-time.
>This would have allowed us to avoid the (very expensive) table lookups
>which we did within the kernel for each transmitted message.

A string lookup though a set of strings fixed at compile time needs to
be rewritten if it is "very expensive."
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
   "All this is conjecture of course, since I *only* post in the nude.
    Nothing comes between me and my t.b.  Nothing."   -- Bill Coderre

bill@robots.oxford.ac.uk (Bill Triggs) (03/05/91)

I have been pleasantly surprised by the basic similarity of so
many of the type-tagging schemes proposed during this
discussion: perhaps those of us who do recognise the need for
such things *CAN* agree on a partial standard until proper
language support is available.

The principal applications of type information seem to be:

(i) Testing whether a Base& or Base* reference actually
references a Derived instance, ie whether a (Derived) cast is
legal.

Type info is *essential* for safe down-casting.  The alternative
of pushing all Derived functions (and implicitly members) into
the Base -- either as full implementations or as empty or
error-calling stubs -- is simply laughable, although it has been
suggested seriously enough by several people during this
discussion.

(ii) Creation of an instance of an implicitly specified class.
For example when reading a persistent object from disk, remotely
creating an object across a network, parsing an object-
specifying input text, or simply creating a local heap object
whose exact type is better not known at compile-time.

It seems that an appropriately global `class-designator' is
indispensable for this, whether string or global address or
global enumeration constant, and that associated with this
must be appropriate `static Class* Class::' or `friend Class*'
new_object() or read_object(), parse_object() functions. The
association can be

  (a) via an explicit switch() on the class-designator (which
  must then be translatable to an enum or const int type)

  (b) via a more dynamic association mechanism such as a
  hash table

  (c) the class-designator can be a reference to a class-type 
  record for the class, which contains the appropriate functions.

We all know the antipolymorphic evils of building explicit type
switches into our code and hashing seems unnecessarily
complicated, so (c) seems the best bet: ie a `class ClassType'
instance for each class, acting as a `static function vtable'.

NB: conditional casts handle only (i) above and are NOT enough
by themselves.

A `standard' type mechanism ought to meet the following
requirements (at least):

(i) Type functions must obviously be lightweight virtual
member functions for most purposes. Given existing virtual
functions the per-instance overhead for type info should be
nil. Non-virtual type functions are not very useful, but
might be allowed for small classes without virtual
functions.

(ii) Derivation as well as exact type must be testable --- via
`isa()', 'isakindof()' or `object >= type' functions --- and
this must work with multiple inheritance (ie it must detect all
potential cast-ability). C++-style type forests must be
supported. It is helpful for the isa() function to return
(void*)this if casting is possible, and 0 otherwise so that
  CLASS* foo = (CLASS*)isa(CLASS::Type)
is possible.

(iii) Switch()-ing on type ought to be possible. For this type
must be convertible to a compile-time-const int. Type must at
least be easily convertible to and from an external, global,
time-invariant const for network/disk-based applications, with
little risk of clashes between different types.

(iv) Type ought to be convertible to a record containing
appropriate static object-creation functions (new_object(),
read_object(), parse_object()), and other useful information
such as class name and schema descriptors.

(v) Even though there is no formal language support,
type-ing a class should be easy & transparent. A single
comprehensible macro call per class would be ideal.


(i),(ii) and (v) are straightforward. Unfortunately, (iii) and
(iv) are difficult to reconcile because some C++ implementations
are very fussy about case label const-ness: in particular g++
(1.39.0) will not accept (casts of) link-time constants such as
const struct or string addresses as labels.  Given that switches
(which work only on exact type) are probably undesirable anyway,
I would vote for type-as-ClassType-record rather than
type-as-enum-int. The record could always contain an enum if
desired.

Of course I wouldn't have bothered to discuss all this if I
didn't have a barrow to push, so here is my sacrificial offering
for the putative standard. It is obviously similar to many
others that have been mentioned, but it seems to fit the
requirements a little bit better than most (IMHO). I declare it
public domain, not subject to patent (at least by me), and free
to all.  There is no guarantee whatsoever, but IMHO it feels
`right' and it has worked well for me for a few months now!.

Comments and unencumbered PD alternatives are solicited for
comparison.

The class ClassType is application dependent, application
defined, and irrelevant to the type-checking mechanism. It
probably contains things like a class-name string or enumeration
const class-token, void* (*)() pointers to `static Class*
Class::' or `friend Class*' object creation and reading
functions, and class schema information. I see no hope of
general agreement on the real contents of this, but an agreed
ClassType base class stub might be useful so that types in
merged applications don't clash fatally (the type system would
then allow safe casting to the true ClassType-type on a
per-instance basis, if ClassType itself had a type!).

=======================================
// V1.0 30/X/90 -*-C++-*-; Bill Triggs (bill@robots.ox.ac.uk) Oxford AGV project.
// type function macros for dynamically-typed classes

#ifndef _typefunctions_h
#define _typefunctions_h 1
#pragma once

#include "ClassType.h"
// For (obj.type() == CLASSNAME::Type) comparisons ClassType should define:
// inline int ClassType::operator==(const ClassType& t) const { return (this == &t); }
// inline int ClassType::operator!=(const ClassType& t) const { return (this != &t); }


#define rootclass(virtual)	typefunctions( ,virtual)
#define subclassof(parent)	typefunctions(|| parent::isa(t), )
#define subclassof2(p1,p2)	typefunctions(|| p1::isa(t) || p2::isa(t), )
#define subclassof3(p1,p2,p3)	typefunctions(|| p1::isa(t) || p2::isa(t) || p3::isa(t), )


#define typefunctions(or_isa_parent,virtual)				\
public:									\
  static const ClassType	Type;					\
  virtual const ClassType&	type() const	{ return Type; }	\
  virtual const void*		isa(const ClassType& t) const	 	\
				  { return ((&t == &Type) or_isa_parent) ? this : 0; }\


#endif _typefunctions_h
=======================================

Use this as follows:

//--- header files -- one macro per class

class Root	{
  rootclass(virtual);  // root class virtual type fns
public:
	....
};

class Tiny	{
  rootclass(inline);  // not-so-useful: non-virtual root class type fns
public:
	....
};

class Derived: public Root {
  subclassof(Root);  // derived class type functions
public:
	....
};

class Derived2: public Root, Tiny {
  subclassof2(Root,Tiny);  // multiply derived class type functions
public:
	....
};

//--- implementation files -- a ClassType CLASS::Type per class

static const ClassType CLASS::Type(.....);


//--- application files --

if (foo.type() != Root::Type) {...}

if (foo.isa(Derived::Type)) { ((Derived)foo).bar(); }

cout << foo.type().classname;

=================================

For those who like switch()-ing on type, or need a global
enum-type constant for network or disk-based type designation, I
also suggest the use of `meaningful 4-char tokens' as below. Not
a foolproof scheme, but a lot safer than an enum with several
people adding to it in different files. Easier to debug too,
especially with a simple routine to print Tokens as strings. The
quotes are a bit messy but there's not a lot of choice if you
need a compile-time constant.

==================================
// V0.0 24/IX/90 -*-C++-*-; Bill Triggs (bill@robots.ox.ac.uk) Oxford AGV project.
// parser token (4 char mnemonic "enumeration type") 

#ifndef _Token_h
#define _Token_h 1
#pragma once

#define CHAR_BITS	8
#define LONG_BITS	32

#if (4 * CHAR_BITS) <= LONG_BITS
typedef unsigned long Token;
#define token(a,b,c,d)	((((a)<<CHAR_BITS | (b))<<CHAR_BITS | (c))<<CHAR_BITS | (d))
#endif

#endif _Token_h
==================================

eg:

const Token HELP = token('H','E','L','P');
Token t;
...
switch(t)
{
case HELP:
  ...
}	

=================================

-- 
Bill Triggs
Oxford University Computing Laboratory
11 Keble Rd
Oxford OX1 3QD, U.K.

peterson@choctaw.csc.ti.com (Bob Peterson) (03/05/91)

In article <27CD159D.6581@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>According to peterson@choctaw.csc.ti.com (Bob Peterson):
>>In article <27C95D3A.1715@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>>>Presumably, a well-designed class hierarchy will use a virtual
>>>function to store objects ...
>>
>>Researchers and vendors have taken a number of implementation approaches
>>...  The details of the various implementations vary widely, but few depend
>>on virtual functions to supply type information.

>Is there any reason to avoid virtual functions for object storage?

>I had not envisioned a virtual function to return type information,
>but rather a virtual function to actually _do_the_work_, so no one
>outside of that function need know the actual type of the object
>stored.  

  Several solutions exist to the problem of persistence.  Basing a
solution on virtual functions is only one approach.  I failed to
clearly make my point: virtual functions are not the only foundation on
which to build persistence.  I know you don't wish to limit the
mechanisms available to implementors of persistence, but the current
language definition does contain hoops through which persistent
implementations must jump.

  Certain requirements that must satisfied by some persistent object
systems, e.g., type evolution, are more significantly more difficult to
satisfy using the virtual function approach.  (Type evolution, i.e.,
changing the definition of a class after instances and modifying the
already saved instances as appropriate, may be substantially easier if
translation between computational and stored formats is done by
functions operating on the class, rather than functions of the class.)
That is, if an instance saved using an out of date definition is read,
either the read must fail or the instance must be translated into the
current definition.  I believe this implies something outside the class
must understand how the class changed, which a member function may have
difficulty doing.

  A number of C++ OODB efforts implement translation using mechanisms
other than member functions.  Some do not implement translation at all.
Of the persistent C++ implementations using the virtual function
approach that I know about, all seem to be "one of" systems, by which I
mean they do not seem intended as general solutions to persistence for
C++.  Indeed, some developers of such solutions now regret taking that
approach, arguing that a more general approach should be used.  Many
took the "virtual function does the work" approach because, at the time
they did their initial design, nothing else was available.

  Solutions to the persistence requirement that don't implement
translation using member functions need some way of identifying the
type of the object being translated.  Over the last few years my group
explored several approaches, including a preprocessor that generated
member functions returning type information with a version specifier,
and a preprocessor that enhanced "new" with a call to update a type
table.  We've also reviewed the member function-based implementations
of Interviews, and similar systems.

  I see the need for a standard mechanism for accessing a class
instance's type.  I believe a general mechanism, similar in spirit to
the property lists found in Lisp, would support not only type
information, but the needs of other enhanced facilities such as
programming environments.  I'd like to see the compiler insert into
each class a static member function that returns a pointer to a singly
linked list of pointers to a class instance.  The class pointed to
would have defined only a virtual function returning a descriptor.  The
descriptor would describe the sort of information the actual instance
contained, e.g., a description of a type.  Users would be able to
derive from the language-specified class description expanded classes
to support 
        * type information at various levels of detail, e.g., a
          compiled form for translation purposes and a source form for
          browsers; 
        * "wrapper" methods (like Lisp's before and after methods);
        * evolution methods, which accept an instance created using a
          previous definition of a class and return an instance usable
          under the current definition;
        * location information if the actual instance is not located in
          the current address space, to enable transparent forwarding in 
          a distributed environment;
        * references to source code, e.g., file name or memory address,
          for use by a programming tool such as Saber C++ and
          Objectworks for C++;
        * etc., etc., etc.  
There would be some cost, probably on the order of one null pointer
per class, if the facility isn't used by a particular application.

>Chip Salzenberg at Teltronics/TCT      <chip@tct.uucp>, <uunet!pdn!tct!chip>
>"It's not a security hole, it's a SECURITY ABYSS." -- Christoph Splittgerber
>   (with reference to the upage bug in Interactive UNIX and Everex ESIX)

Bob Peterson                Compuserve: 70235,326          Expressway Site,
Texas Instruments           Internet: peterson@csc.ti.com   North Building,
P.O. Box 655474, MS238      Landline: (214) 995-6080         2nd Floor,
Dallas, Texas, USA 75265                                      CSC Aisle C3
Bob Peterson                Compuserve: 70235,326          Expressway Site,
Texas Instruments           Internet: peterson@csc.ti.com   North Building,
P.O. Box 655474, MS238      Landline: (214) 995-6080         2nd Floor,
Dallas, Texas, USA 75265                                      CSC Aisle C3

chip@tct.uucp (Chip Salzenberg) (03/06/91)

According to bill@robots.oxford.ac.uk (Bill Triggs):
>Type info is *essential* for safe down-casting.  The alternative
>of pushing all Derived functions (and implicitly members) into
>the Base -- either as full implementations or as empty or
>error-calling stubs -- is simply laughable...

"Laughable" is not a very helpful description.  Specific objections,
please.

Note that downcasting to Derived* can itself be _one_ virtual function
of Base.

>Creation of an instance of an implicitly specified class....
>It seems that an appropriately global `class-designator' is
>indispensable for this, whether string or global address or
>global enumeration constant ...

The class name is already guaranteed to be unique (name equivalence
and all that).  What need of more?

>... appropriate `static Class* Class::' or `friend Class*'
>new_object() or read_object(), parse_object() functions...

What's missing from a virtual function "Base *Base::clone() const"?

>... Switch()-ing on type ought to be possible.

I fail to see why such a feature should be supported, when the
difficulties of producing a global (multi-module) class->integer
equivalence are so well known.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
   "All this is conjecture of course, since I *only* post in the nude.
    Nothing comes between me and my t.b.  Nothing."   -- Bill Coderre

jimad@microsoft.UUCP (Jim ADCOCK) (03/07/91)

In article <1991Mar5.143615.5847@csc.ti.com> peterson@choctaw.csc.ti.com (Bob Peterson) writes:
|  I see the need for a standard mechanism for accessing a class
|instance's type.  I believe a general mechanism, similar in spirit to
|the property lists found in Lisp, would support not only type
|information, but the needs of other enhanced facilities such as
|programming environments.  I'd like to see the compiler insert into
|each class a static member function that returns a pointer to a singly
|linked list of pointers to a class instance.  The class pointed to
|would have defined only a virtual function returning a descriptor.  The
|descriptor would describe the sort of information the actual instance
|contained, e.g., a description of a type.  Users would be able to
|derive from the language-specified class description expanded classes

....

Seems we're getting hung up arguing details of compiler implementation
rather than issues of language definition.  How should run-time
type information be represented in the language -- as opposed to in the
resulting executable? 

chip@tct.uucp (Chip Salzenberg) (03/07/91)

According to peterson@choctaw.csc.ti.com (Bob Peterson):
>Virtual functions are not the only foundation on which to build
>persistence.

Quite.  "There's more than one way to do it."  [tm] Larry Wall.

>That is, if an instance saved using an out of date definition is read,
>either the read must fail or the instance must be translated into the
>current definition.  I believe this implies something outside the class
>must understand how the class changed, which a member function may have
>difficulty doing.

I am not persuaded that this conclusion is necessary.  _Something_
must know about the changes; why not the class itself?  In other
words, what is the difficulty with giving to each class the
responsibility for remembering its own revision history?  I'd like to
hear specific technical problems.

>Solutions to the persistence requirement that don't implement
>translation using member functions need some way of identifying the
>type of the object being translated.

What is wrong with the simple "ClassName:version"?

>I see the need for a standard mechanism for accessing a class
>instance's type.

I fail to see any benefit such a feature would provide for
persistence, since other information essential for freezing an object
(private member variables, for instance) will be unavailable from the
outside.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
   "All this is conjecture of course, since I *only* post in the nude.
    Nothing comes between me and my t.b.  Nothing."   -- Bill Coderre

beard@ux5.lbl.gov (Patrick C Beard) (03/07/91)

The information about what class an object is can be easily obtained by using
the pointer to the object's virtual function table as the class id, which
whill always be unique for every class.  This could be quite easy to add
to the language and easy to implement.  Class relationships would be easy
to implement by putting a link field in the vtable which points to a
class's super-class.

I agree that using run-time type information is quite easy to abuse, but
then so is multiple-inheritance.  These are powerful tools, but we shouldn't
be forced to live without them.

--
//  Patrick C. Beard, Software Engineer, Berkeley Systems, Inc.
//                    "Heroes of technology."
//   beard@lbl.gov, d0346@applelink.apple.com (ATTN: Patrick)

dsouza@gwen.cad.mcc.com (Desmond Dsouza) (03/08/91)

In article <10721@dog.ee.lbl.gov> beard@ux5.lbl.gov (Patrick C Beard) writes:

  >   The information about what class an object is can be easily obtained by using
  >   the pointer to the object's virtual function table as the class id, which
  >   whill always be unique for every class.  This could be quite easy to add
  >   to the language and easy to implement.  Class relationships would be easy
  >   to implement by putting a link field in the vtable which points to a
  >   class's super-class.

Unfortunately, most current compilers do not guarantee a single vtable
for each class, given separate compilation in a traditional
compile-link-load environment.

Desmond.
--

-------------------------------------------------------------------------------
 Desmond D'Souza, MCC CAD Program | ARPA: dsouza@mcc.com | Phone: [512] 338-3324
 Box 200195, Austin, TX 78720 | UUCP: {uunet,harvard,gatech,pyramid}!cs.utexas.edu!milano!cadillac!dsouza

peterson@choctaw.csc.ti.com (Bob Peterson) (03/08/91)

  I should point out that for purposes of this discussion I assume an
OODB in which changing a class instance to conform to a revised
definition is done "as needed," rather than "all at once," or batch. 
In the latter environment a utility would carry out the changes, all
programs impacted are immediately recompiled, and the issue of runtime
type information becomes moot.

  Also, almost the same set of issues arise when considering movement
of a class instance into or out of an address space, including movement
for persistence and for interprocess communication.

In article <27D56A26.2881@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>According to peterson@choctaw.csc.ti.com (Bob Peterson):
>>That is, if an instance saved using an out of date definition is read,
>>either the read must fail or the instance must be translated into the
>>current definition.  I believe this implies something outside the class
>>must understand how the class changed, which a member function may have
>>difficulty doing.
>
>I am not persuaded that this conclusion is necessary.  _Something_
>must know about the changes; why not the class itself?  In other
>words, what is the difficulty with giving to each class the
>responsibility for remembering its own revision history?  I'd like to
>hear specific technical problems.

  Technical problems arise in several areas.  The first two are hard
issues, while the second two seem easier to solve.

  * Certain properties of an object's representation depend on the
specific runtime environment.  Examples include 
	the implementation details of virtual functions,
	the implementation details virtual inheritance,
	how ordering of the names of inherited classes impacts the 
	    layout of local and inherited member variables,
	how the compiler chooses to order the noncontiguous parts 
	    of the class' state, and
	where and how much padding is inserted to obtain correct
	    alignment.
Translating from external format to internal format must take all of
these factors into account.  Any of the above can change without
changing the meaning of a program, and, quite correctly, the language
definition excludes specification of any of them.  Do we wish to
require the class implementation to understand all possible
implementations of the above properties, past as well as present?  I
believe such details, which are not part of the language, should not be
reflected in the class.  They must be taken care of outside the class,
i.e., by an external translation mechanism.  (I do assume we expect the
class itself to be portable, as well as instances of the class.)

  * Certain kinds of type evolution occur outside the immediate class.
Examples of such changes include changing an ancestor class's
inheritance list, and changing from ordinary to virtual inheritance. 
While such changes can alter the immediate class's state, forcing
recognition of the change in the immediate class may be undesirable.
For example, a class should not care about insertion of an additional
privately inherited class in an ancestor.  Forcing such recognition
could easily result in substantial maintenance, an effort not required
if a different approach is used.

  * If I need to add a class library to my development environment I
must somehow add the appropriate translation methods to the library. 
This implies I must have source code.  If instead of using methods, the
system derives translation data from header files, I _might_ be able to
use a binary distribution of the library.  Requiring source code
changes precludes using a library available to me only as a binary.

  * How many prior versions must a class know about?  In the real
world, the number of revisions will certainly be larger than one, and
could easily exceed 20 over the lifetime of a database.  As revisions
accumulate the evolution function(s) embedded in the class will become
large.  (This isn't really a technical problem, but a management
problem, I confess, but I believe it is relevant.)

  For a good discussion of over 20 ways to change a class, and the
meaning of each change, see "Semantics and Implementation of Schema
Evolution in Object-Oriented Databases," Banerjee & Kim, Proceedings of
SIGMOD 1987, pp. 311-322.  While the paper discusses a Lisp-based OODB,
it is quite relevant to C++ class evolution.  About one quarter of the
changes in their list do not show up in the immediate class.

>>I see the need for a standard mechanism for accessing a class
>>instance's type.
>
>I fail to see any benefit such a feature would provide for
>persistence, since other information essential for freezing an object
>(private member variables, for instance) will be unavailable from the
>outside.

  Why do you assume the absence of a data dictionary?  In fact, most
existing OODB implementations use a data dictionary, i.e., a complete
description of all data stored in the database.  Frequently a utility
program initially loads the dictionary from information in header
files.  That same utility should be able to detect changes in a class
definition, no matter what the origin of the change.

  More advanced dictionaries support browsing of the definitions. 
Future dictionaries will feature interactive interfaces.  Developers
will make class changes through the dictionary, with interactive
support for visualizing the impact of the changes, as well as
dictionary support for ensuring the changes reflect correct semantics
and are supportable by the translation mechanism.

  Given a data dictionary, when an application runs and fetches an
object saved using a now out of date definition the OODB runtime can
consult the dictionary to figure out what changes must be made in order
to correctly utilize the object under the definition known to the
application.  The OODB runtime may alter the translation of the
object's state, providing default values for added data members,
connecting as appropriate to the language's virtual function mechanism,
etc.  

>-- 
>Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
>   "All this is conjecture of course, since I *only* post in the nude.
>    Nothing comes between me and my t.b.  Nothing."   -- Bill Coderre

    Bob

Bob Peterson                Compuserve: 70235,326          Expressway Site,
Texas Instruments           Internet: peterson@csc.ti.com   North Building,
P.O. Box 655474, MS238      Landline: (214) 995-6080         2nd Floor,
Dallas, Texas, USA 75265                                      CSC Aisle C3

jbuck@galileo.berkeley.edu (Joe Buck) (03/09/91)

In article <10721@dog.ee.lbl.gov>, beard@ux5.lbl.gov (Patrick C Beard) writes:
|> The information about what class an object is can be easily obtained by using
|> the pointer to the object's virtual function table as the class id, which
|> whill always be unique for every class.  This could be quite easy to add
|> to the language and easy to implement.  Class relationships would be easy
|> to implement by putting a link field in the vtable which points to a
|> class's super-class.

False.  Several reasons:

1). The language standard says nothing about virtual function tables.
It only specifies the effect of calling virtual functions.  A different
data structure could be used, and in some cases might be more efficient.

2).  You can't count on testing two objects for the same type by comparing
their vtbl pointers anyway.  The compiler may have had to duplicate the
vtbl; for example, because of incremental linking or because it didn't
have enough information to avoid generating the table more than once.



--
Joe Buck
jbuck@galileo.berkeley.edu	 {uunet,ucbvax}!galileo.berkeley.edu!jbuck	

chip@tct.uucp (Chip Salzenberg) (03/09/91)

According to beard@ux5 (Patrick C Beard):
>The information about what class an object is can be easily obtained by using
>the pointer to the object's virtual function table as the class id, which
>will always be unique for every class.

Current implementations of C++ may have more than one copy of the
virtual function table for any given class.  Sorry.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
 "Most of my code is written by myself.  That is why so little gets done."
                 -- Herman "HLLs will never fly" Rubin

rfg@NCD.COM (Ron Guilmette) (03/10/91)

Regarding using numeric codes for run-time type identifiers:

In article <27D270A7.2E3B@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>
>Why not use the class name itself?

One reason is that `class C' or `struct S' can have two or more meanings.
Each of several compilations units may define its own `class C'.

>>A much cleaner solution would have been to enlist the compiler & linker
>>to assign the globally unique "type codes" up front, prior to run-time.
>>This would have allowed us to avoid the (very expensive) table lookups
>>which we did within the kernel for each transmitted message.
>
>A string lookup though a set of strings fixed at compile time needs to
>be rewritten if it is "very expensive."

You don't understand.  If somebody charges you only $2.50 per ton of
air, it is still "too expensive".  Anything is "expensive" if you don't
really need it.

-- 

// Ron Guilmette  -  C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// New motto:  If it ain't broke, try using a bigger hammer.

rfg@NCD.COM (Ron Guilmette) (03/10/91)

In article <11792@pasteur.Berkeley.EDU> jbuck@galileo.berkeley.edu (Joe Buck) writes:
+In article <10721@dog.ee.lbl.gov>, beard@ux5.lbl.gov (Patrick C Beard) writes:
+|> The information about what class an object is can be easily obtained by using
+|> the pointer to the object's virtual function table as the class id, which
+|> whill always be unique for every class...
+
+False.  Several reasons:
...
+2).  You can't count on testing two objects for the same type by comparing
+their vtbl pointers anyway.  The compiler may have had to duplicate the
+vtbl; for example, because of incremental linking or because it didn't
+have enough information to avoid generating the table more than once.

And this happens quite often!  Addresses of vtables CANNOT BE USED as
globally unique class identifiers.  Period.

-- 

// Ron Guilmette  -  C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// New motto:  If it ain't broke, try using a bigger hammer.

rfg@NCD.COM (Ron Guilmette) (03/10/91)

In article <27D3E544.619A@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
+According to bill@robots.oxford.ac.uk (Bill Triggs):
+>Creation of an instance of an implicitly specified class....
+>It seems that an appropriately global `class-designator' is
+>indispensable for this, whether string or global address or
+>global enumeration constant ...
+
+The class name is already guaranteed to be unique (name equivalence
+and all that).  What need of more?

Not only are file-scope class names not guaranteed to be unique across
multiple independent compilation units (which may later be combined to
form a single program) but class names need not even be unique *within*
a single compilation unit.  (Remember that any given compilation unit may
contain numerous independent declarative scopes.)
-- 

// Ron Guilmette  -  C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// New motto:  If it ain't broke, try using a bigger hammer.

peterson@choctaw.csc.ti.com (Bob Peterson) (03/11/91)

In article <71091@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
>In article <1991Mar5.143615.5847@csc.ti.com> peterson@choctaw.csc.ti.com (Bob Peterson) writes:
>|  I see the need for a standard mechanism for accessing a class
>|instance's type.  I believe a general mechanism, similar in spirit to
>|the property lists found in Lisp, would support not only type
>|information, but the needs of other enhanced facilities such as
>|programming environments.  I'd like to see the compiler insert into
>|each class a static member function that returns a pointer to a singly
>|linked list of pointers to a class instance.  The class pointed to
>|would have defined only a virtual function returning a descriptor.  The
>|descriptor would describe the sort of information the actual instance
>|contained, e.g., a description of a type.  Users would be able to
>|derive from the language-specified class description expanded classes
>
>....
>
>Seems we're getting hung up arguing details of compiler implementation
>rather than issues of language definition.  How should run-time
>type information be represented in the language -- as opposed to in the
>resulting executable? 

  Sorry, I misspoke.  My phrasing above, using the word "compiler,"
mislead you.  

  I intended to suggest the _language_ be enhanced to require a
conforming language processor to predefine in each class a static
member function, which returns the list described above.  Also added
would be a predefined interface through which a user program could add
application-specific elements onto the list.  The language would
predefine some descriptor values, reserve additional values for future
enhancements, and reserve a range of values for user-defined (and
nonstandard) functionality.  Similarly, a conforming language processor
would be required to implement certain descriptors, and would be
generate others based on user-specified options, i.e., when the user
asks for them.

  The spirit of my suggestion was along the lines John Interrante and
Mark Linton suggests in their "Runtime Access to Type Information in
C++" paper (in the Proceedings of the 1990 USENIX C++ Conference, pp.
233-240).  The paper describes "Dossier," and experimental experience
using the idea in Interviews' Unidraw library.

  However, I would like to extend the idea to cover a variety of future
needs, not just the single idea of runtime type information.  In
effect, I want an extensible mechanism able to supply a variety of
metaclass information.  I also suggest defining a static function,
rather than a member variable, for additional flexibility, and to avoid
the "sometimes it is virtual, other times not" suggestion made in the
above paper.

    Bob
Bob Peterson                Compuserve: 70235,326          Expressway Site,
Texas Instruments           Internet: peterson@csc.ti.com   North Building,
P.O. Box 655474, MS238      Landline: (214) 995-6080         2nd Floor,
Dallas, Texas, USA 75265                                      CSC Aisle C3

chip@tct.uucp (Chip Salzenberg) (03/12/91)

According to peterson@choctaw.csc.ti.com (Bob Peterson):
>In article <27D56A26.2881@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>>What is the difficulty with giving to each class the responsibility for
>>remembering its own revision history?  I'd like to hear specific technical
>>problems.
>
>  * Certain properties of an object's representation depend on the
>    specific runtime environment.  Examples include 
>	the implementation details of virtual functions,
>	the implementation details virtual inheritance,
>    ...
>    Translating from external format to internal format must take all of
>    these factors into account.

But these issues shouldn't enter into the discussion at all!

Any implementation of persistent objects for C++ will presumably be
designed to require no more of its environment than the presence of a
C++ language compiler/interpreter.  In particular, it will not need to
know any details of the C++ implementation.  For the persistent-object
support to be aware of the details of a particular C++ implementation
is, in my opinion, a design error.

In any case, the C++ implementation details are not relevant to the
question of how a virtual member function is at a disadvantage
compared to a non-member function in freezing and restoring objects,
which is what I asked about.

>  * Certain kinds of type evolution occur outside the immediate class.
>Examples of such changes include changing an ancestor class's
>inheritance list, and changing from ordinary to virtual inheritance.

Such type evolution must be dealt with by any store/retrieve function,
whether it be a virtual member function or some other kind.

Realize that, as I envision it, the virtual functions for storage and
retrieval will be generated automatically from the data dictionary.
That same data dictionary will also be used to generate the C++ class
definition.  Internal and external type evolution are handled equally
well by such a system.

>  * If I need to add a class library to my development environment I
>must somehow add the appropriate translation methods to the library. 
>This implies I must have source code.

Yes -- iff you desire to create persistent objects of library-defined
types.  I would avoid this need by (1) getting source code, which IMHO
is vital for C++ programming anyway, or (2) creating persistent
classes that contain or derive from the library-defined classes.

>  * How many prior versions must a class know about?  ...  As revisions
>    accumulate the evolution function(s) embedded in the class will become
>    large.

True but irrelevant to the virtual-vs.-friend issue.

>> ... other information essential for freezing an object
>>(private member variables, for instance) will be unavailable
>>from the outside.
>
>  Why do you assume the absence of a data dictionary?

In fact, I did assume a data dictionary for my approach described
earlier.  I just didn't think of it when I wrote the above sentence.

>The OODB runtime may alter the translation of the object's state,
>providing default values for added data members, connecting as
>appropriate to the language's virtual function mechanism, etc.

To suggest that information and procedures of this kind belongs
anywhere _except_ the class definition is, IMO, preposterous.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
 "Most of my code is written by myself.  That is why so little gets done."
                 -- Herman "HLLs will never fly" Rubin

cok@islsun.Kodak.COM (David Cok) (03/12/91)

In article <4338@lupine.NCD.COM> rfg@NCD.COM (Ron Guilmette) writes:
>In article <27D3E544.619A@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>+According to bill@robots.oxford.ac.uk (Bill Triggs):
>+>Creation of an instance of an implicitly specified class....
>+>It seems that an appropriately global `class-designator' is
>+>indispensable for this, whether string or global address or
>+>global enumeration constant ...
>+
>+The class name is already guaranteed to be unique (name equivalence
>+and all that).  What need of more?
>
>Not only are file-scope class names not guaranteed to be unique across
>multiple independent compilation units (which may later be combined to
>form a single program) but class names need not even be unique *within*
>a single compilation unit.  (Remember that any given compilation unit may
>contain numerous independent declarative scopes.)
>-- 

I agree that class names can be reused in local scopes within functions and
other classes.   But can they be reused at file scope in different 
compilation units?  I thought these were effectively global, as free
functions are.  If you try this with Sun C++ (and the classes happen to
have non-inline functions with the same name and parameter types) you
will get a link error with a multiply defined function error.  Not to
necessarily advocate the use of a class name as a type tag, but it seems
that it would work, with suitable mangling for local scopes, if one
wanted to do it.

David R. Cok
Eastman Kodak Co.
cok@Kodak.COM

chip@tct.uucp (Chip Salzenberg) (03/13/91)

According to rfg@NCD.COM (Ron Guilmette):
>In article <27D3E544.619A@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
>+The class name is already guaranteed to be unique (name equivalence
>+and all that).  What need of more?
>
>Not only are file-scope class names not guaranteed to be unique across
>multiple independent compilation units (which may later be combined to
>form a single program) but class names need not even be unique *within*
>a single compilation unit.

Quite right; I had forgotton about local types.

On the other hand, I do not believe that avoidance of local types for
persistent and/or transmitted data is too much to ask of a programmer,
especially since such a restriction would avoid the need for two class
names (lexical and unique).
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
 "Most of my code is written by myself.  That is why so little gets done."
                 -- Herman "HLLs will never fly" Rubin

rfg@NCD.COM (Ron Guilmette) (03/25/91)

In article <1991Mar12.123811.13701@kodak.kodak.com> cok@islsun.Kodak.COM (David Cok) writes:
+In article <4338@lupine.NCD.COM> rfg@NCD.COM (Ron Guilmette) writes:
+>In article <27D3E544.619A@tct.uucp> chip@tct.uucp (Chip Salzenberg) writes:
+>+According to bill@robots.oxford.ac.uk (Bill Triggs):
+>+>Creation of an instance of an implicitly specified class....
+>+>It seems that an appropriately global `class-designator' is
+>+>indispensable for this, whether string or global address or
+>+>global enumeration constant ...
+>+
+>+The class name is already guaranteed to be unique (name equivalence
+>+and all that).  What need of more?
+>
+>Not only are file-scope class names not guaranteed to be unique across
+>multiple independent compilation units (which may later be combined to
+>form a single program) but class names need not even be unique *within*
+>a single compilation unit.  (Remember that any given compilation unit may
+>contain numerous independent declarative scopes.)
+>-- 
+
+I agree that class names can be reused in local scopes within functions and
+other classes.   But can they be reused at file scope in different 
+compilation units?

Why ask me?  Why not try it yourself?  You do have a C++ compiler (or
translator) don't you?

+I thought these were effectively global, as free
+functions are.

What is a "free function"?  I never heard of such a thing before.  Most
function I have seen are quite expensive. :-)

+If you try this with Sun C++ (and the classes happen to
+have non-inline functions with the same name and parameter types) you
+will get a link error with a multiply defined function error.

Yes.  On every other Thursday, if there is a full-moon and if a dog
is howling, you will get a link-time error.  In general however, you
will not.

+Not to
+necessarily advocate the use of a class name as a type tag, but it seems
+that it would work, with suitable mangling for local scopes, if one
+wanted to do it.

Well you could try to use tags a "globally" unique identifiers (across
compilation units) for their respective classes, but that would tend to
be unsafe.

I think that there are safer (and more efficient) ways to provide globally
unique "type identifiers" for class types.
-- 

// Ron ("Shoot From The Hip") Guilmette
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// New motto:  If it ain't broke, try using a bigger hammer.

chip@tct.uucp (Chip Salzenberg) (03/28/91)

According to rfg@NCD.COM (Ron Guilmette):
>According to cok@islsun.Kodak.COM (David Cok):
>+I agree that class names can be reused in local scopes within functions and
>+other classes.   But can they be reused at file scope in different 
>+compilation units?
>
>Why ask me?  Why not try it yourself?  You do have a C++ compiler (or
>translator) don't you?

We have progressed beyond the point of "My compiler allows X,
therefore it's legal", haven't we?

The ARM explicitly notes its dependence on name equivalence.  Flouting
that rule is just asking for trouble.
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
   "All this is conjecture of course, since I *only* post in the nude.
    Nothing comes between me and my t.b.  Nothing."   -- Bill Coderre