[gnu.g++] Chameleon objects

keith@csli.Stanford.EDU (Keith Nishihara) (12/05/89)

When a class has a constructor and virtual functions, cfront1.2 generates
code within the constructor function to set up the virtual function pointers.
If a class is derived from such a base class, and one of the virtual
functions is called from the base class constructor, the wrong virtual
function is called, since during the execution of the base class constructor,
the virtual function pointer table is set up as for the base class,
and is not changed to show the derived class virtual functions until
the derived class constructor is entered.

Here is a real case:

    I have a hierarchical graphical editor in which the user
    manipulates prototypical objects in a layout.  There are several
    types of primitive proto objects, and also compound proto objects
    (which represent occurrences of other layouts included within a
    higher level layout).  These proto objects are derived from a base
    class `proto'.  In order to support execution, part of the objects
    state is represented as an `instance' class;  primitive proto
    classes create instance classes of themselves for each occurrence
    of a compound proto object representing the layout including the
    primitive proto.

    The implementation of this requires that each proto class define
    a virtual function Instantiate(context), which creates an instantiation
    in the given context.  *All* derived classes are required to have
    at least one instance, defined within a context called top_level.
    The most natural way to achieve this is to have the constructor
    for the base class call the virtual function Instantiate().
    However, at the time that this virtual function is called, the virtual
    function table is set up as if for the base class, and not for the
    derived class.

    class proto
    {
    public:
	proto() { ... Instantiate(top_level); ... }
	virtual void Instantiate(context) { }	// Empty.
	...
    };

    class type_a_proto : public proto
    {
    public:
	virtual void Instantiate(context) { <create instance> }
    };

    What happens is that the empty base class Instantiate function
    is called instead of the type_a_proto::Instantiate function when
    a new type_a_proto is created.

    (The fix, of course, is to call the Instantiate function from each
    derived constructor, instead of the base class.  The problem is that
    it is then not possible to derive further sub classes of a derived type,
    as the intermediate class will create unwanted instantiations of
    themselves as their intermediate constructor functions are called.)

Is this reasonable semantics for virtual function calling?  I know that it
is somewhat bogus to do anything with the derived object before its
constructor has been called, but I do not find anything which says
that it is illegal.  In this case, my virtual function calls are not
actually operating on the derived object, but are rather creating a
parallel structure in a different context.

Do other C++ compilers exhibit the same behaviour?

Neil/.		Neil%teleos.com@ai.sri.com	...decwrl!argosy!teleos!neil