Newsgroups: comp.lang.c++
Path: utzoo!censor!geac!alias!news
From: Reid Ellis <rae@utcs.toronto.edu>
Subject: Re: asking an object for its type
Message-ID: <1991Feb28.055525.24273@alias.uucp>
Sender: Reid Ellis <rae%alias@csri.toronto.edu>
Reply-To: Reid Ellis <rae@utcs.toronto.edu>
Organization: Alias Research, Inc. Toronto ON Canada
References: <23984@netcom.COM> <1190@sheol.UUCP> <1991Feb19.000449.22255@gpu.utcs.utoronto.ca> <607@taumet.com> <65451@brunix.UUCP> <11284@pasteur.Berkeley.EDU> <1991Feb21.182349.19132@gpu.utcs.utoronto.ca>
Date: Thu, 28 Feb 91 05:55:25 GMT

In <1991Feb21.182349.19132@gpu.utcs.utoronto.ca> craig@gpu.utcs.utoronto.ca (Craig Hubley) writes:
>Right on.  This why a simple typeof() won't do either.  It is possible
>to build a more consistent way to tell what an object can really do,
>(e.g. x.hasmember("diameter")) but this is only marginally better since
>you are still requiring code to be rewritten and deal with the names,
>which you supposedly already dealt with in the class header.
>
>As you point out, this form of programming is unreliable unless others
>consistently point out your convention.  Is has a far worse flaw, however,
>which also applies to defining a virtual function normally:  it is not
>possible for the base class programmer to anticipate everything that will
>be done by the derived class programmers.  It is ridiculous to say that
>the solution is to add a diameter of null value, or a virtual that does
>nothing, in the base type "shape".  It is CIRCLE's problem, but as C++
>works now CIRCLE can't solve it.  So shape ends up dealing with all of its
>derived type's problems.  And code reusability is a myth.

The below solution has the two advantages of (a) being very
straightforward to code and understand, and (b) being user-extensible
in a hierarchical manner.

shape.h:
	// I know the 'extern's aren't neccessary, but I like them.
	//
	extern class Circle;
	extern class UserDefined;

	// I like 'struct' too, so sue me
	//
	struct Shape {
		virtual Circle *asCircle() { return NULL; }
		virtual UserDefined *asUserDefined() { return NULL; }
	}

circle.h:
	struct Circle : Shape {
		Circle *asCircle() { return this; }
	}

myUserCode.h:
	extern class Whatever;

	struct UserDefined : Shape {
		UserDefined *asUserDefined() { return this; }
		virtual Whatever* asWhatever() { return NULL; }
	}

This way, if you get a Shape '*s', you can say:
	Circle *c;
	if((c = s->asCircle()) != NULL) { // do circle things..

	UserDefined *ud;
	Whatever *w;
	if((ud = s->asUserDefined()) != NULL && (w = wd->asWhatever()) != NULL)
	{ // do Whatever things..

Actually, if you want to define these things as *never* being allowed
to fail [i.e. they will only be called when the application's state
*DEMANDS* that they be the type requested, then rather than returning
NULL, you can make the base class ASSERT fail, or perr() or whatever.
Then you can write code like:

	shape->asUserDefined()->asWhatever()->whateverMethod(param);

Yes, it's typesafe downcasting..

						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
