From: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Subject: Re: Object metamorphosis - can/should it be done?
Organization: Computer Science, University of Melbourne, Australia
Date: Tue, 18 Aug 1992 05:43:36 GMT

swillden@news.ccutah.edu (Shawn Willden) writes:

>I ran across some interesting Objective-C code the other day and I 
>wondered if there is some way to implement it in C++.  Then I wondered
>if it was a good idea to try.  Anyway, the concept is that an object
>can be asked to mutate into an object of a different type.  Under
>Objective-C's concept of an object, any object can mutate to essentially
>any other type because all messages are resolved at run-time.  In C++'s
>static system this is not possible, however, it seems to me that there 
>is at least one case in which it might be possible in C++ and maybe even 
>useful.  Given a base class pointer or reference to a derived class
>object, it should be possible to replace the derived class object with
>an object of another derived type.  

One major problem with your example code

>void Pupa::metamorphose() {
>	Pupa* old_me = this;
>	(Adult*)this = new Adult(*old_me);
>	delete old_me;
>}

is that "this" is passed by value, not by reference, so assignment to "this"
is not going to have the desired effect.

What you want is for Butterfly to contain a pointer to ButterflyStage.
The Butterfly class should then be a 'smart reference' to that ButterflyStage.
Each stage should have a metamorphosis() function that returns the next
stage. The metamorphose() function will then reassign the pointer with a
newly allocated stage pointer returned from metamorphosis().

Here's how to do it in standard C++:

class ButterflyStage {
	//...
public:
	ButterflyStage()		{...}
	ButterflyStage(Butterfly& b)	{...}
	virtual void report()=0;
	virtual int  can_fly()=0;
	//...

	virtual ButterflyStage* metamorphosis()=0;
};

class Pupa : public Butterfly {
public:
	Pupa()			{ }
	void report()		{ cout << "I'm a caterpillar\n"; }
	int  can_fly()		{ return 0; }
	ButterflyStage* metamorphosis() { return new Adult; }
};

class Adult : public Butterfly {
public:
	Adult()			{ }
	void report()		{ cout << "I'm a real butterfly\n"; }
	int  can_fly()		{ return 1; }
	ButterflyStage* metamorphosis() { return new Adult; }
				// maybe we should "return new Corpse" here :-)
};

class Butterfly {
	ButterflyStage *stage;
public:
	Butterfly(ButterflyStage *s) : stage(s) {}
		// NB stage must be allocated with new!
	Butterfly() : stage(new Pupa) {}		// optional default
	~Butterfly() { delete stage; }
	Butterfly(const Butterfly& b) {...}

	void metamorphose() {
		ButterflyStage* next = stage->metamorphosis();
		delete stage;
		stage = next;
	}

	// you have to write forwarding functions for every function
	// in class ButterflyStage ... 
	void report() { stage->report(); }
	int can_fly() { return stage->can_fly(); }
	// ...
};

main() {
	Butterfly fred(new Pupa);
	fred.report();
	fred.metamorphose();
	fred.report();
}

This gives you the desired output:

>I'm a caterpillar
>I'm a real butterfly

Aside: this is another example where we want to do smart references.
A Butterfly is a smart reference to a ButterflyStage.
I could post code to show how to do it using overloading of operator
dot as per Jim Adcock's proposal, but since I am probably the ONLY
person in possession of a compiler that implements his proposal (I have
modified gcc 2.2.2 to do this) there isn't much point.

>Shawn Willden
>swillden@icarus.weber.edu	

-- 
Fergus Henderson             fjh@munta.cs.mu.OZ.AU      
This .signature VIRUS is a self-referential statement that is true - but 
you will only be able to consistently believe it if you copy it to your own
.signature file!
Newsgroups: comp.lang.c++