Topic: object type and ancestor/super key-word
Author: hugp@swell.actrix.gen.nz (Peter Hug)
Date: Wed, 13 Oct 1993 03:50:08 GMT Raw View
Hi C++ers
I am designing and coding in C++ for over five years. During this time,
and having also had extensive exposure to pure OOP environments like
Smalltalk and Actor, my wishlist for alterations to the language has
grown significntly. In this article, I like to put forward two suggestions
for enhancements to the C++ language. I have selected these two suggestions
simply because I am expecting the objections to be less numerous than this
could be the case with others.
Sugestion 1:
------------
"super" or "ancestor" keyword: When early binding an ancestors method, the
ancestors class name must be supplied. When the hierarchy is altered or
the ancestors class name changed, descendent classes may be affected. The
implementation of a Smalltalk like super key word (or perhaps ancestor)
would allow me to invoke the ancestors method via this keyword. Consider:
class A
{
void method1(void);
};
class B : public A
{
void method1(void)
{
// do whatever needs doing here
// ...and then spawn the ancestors method1...
//
A::method1(); // That's how we commonly do it today
this->ancestor::method1(); // And that would be more like me
}
};
The proposed type of invocation solves two problems:
1. If class B inherits from another class or if the name of class A
changes, B::method1() is not affected.
2. If class A inherits from class AA, and class A does not implement
A::method1() but inherits AA::method1(), an early bound method
invocation via A::method1() in B::method1()'s body is ambiguous
to humans reading the code since A::method1() does not exist (albeit
my compilers are able to interpret this correctly).
Sugestion 2:
------------
object type: My own class library provides a common root class (Object).
This is not the case with most class libraries I have been exposed to. This
often causes headaches. Assume my system is composed of objects belonging
to independant class hierarchies. I now wish to implement a class with the
purpose to notify other objects of certain events (e.g. a Timer class that
notifies owners of timer-events). Objects of that class would have knowledge
of the client and it's notifier member function. If I have a common root
(i.e. just one class hierarchy exists) the owner is always a root type
(e.g. an Object*) and everything works just fine.
However if we need to store a pointer to an object where it is not clear
to which hierarchy the object belongs, we would have to store the pointer
as a void pointer and would not be able to create a genericly applicable
timer class for example. If we had an object pointer, we could do some-
thing like:
typedef void (object::*X_NOTIFIER_FUNC)(X*);
class A { void notifier(X*); };
class B { void notifier(X*); };
class X
{
object * theOwner;
X_NOTIFIER_FUNC theNotifier;
X(object * owner, X_NOTIFIER_FUNC notifier) {
theOwner = owner;
theNotifier = notifier;
}
void processEvent (void) {
if (theOwner && theNotifier)
theOwner->*theNotifier(this);
}
};
As you can see, class X is now generically usefull, i.e. any class that
provides an appropriate notifier function can be X's client. (Well try
to replace the object type in the above example with the void type!)
Of course, I would expect compilers to ensure that the object type could
ever only be assigned objects, i.e.:
class Z;
int x;
void * y;
Z * z;
object * pObject;
pObject = &x; // ==> Error: Can not cast int * to object *
pObject = y; // ==> Error: Can not cast void * to object *
pObject = (object *) &x; // ==> Error: Can not cast int * to object *
pObject = (object *) y; // OK
pObject = z; // OK
// Assume (pObject == z):
delete pObject; // Should now behave like: delete z;
I am looking forward to an interesting debate!
======================================================================
Peter Hug + "Rather the sparrow in the pan
c/- Maxi Solutions Ltd + than the pigeon on the roof."
Paraparaumu Beach +
New Zealand +
+
Voice: 61-4-2983441 +
Fax: 61-4-2972288 +
+
e-mail: hugp@swell.actrix.gen.nz +
======================================================================
Author: dag@control.lth.se (Dag Bruck)
Date: 13 Oct 1993 06:50:09 GMT Raw View
Ancestor/super keyword.
You can get almost all of the benefits of a new keyword by adding a a
typedef to all your derived classes. For example:
class A { ... }; // base class
class B : public A {
typedef A ancestor;
public:
// ...
};
Now you can write
ancestor::f();
in B to call A's f().
When you change the class hierarchy, you also have to change the
typedef, but it is a small inconvenience. Trick originally suggested
by Mike Tiemann.
-- Dag
Author: USER@zew.zew-mannheim.de (Michael Luebbeke)
Date: Wed, 13 Oct 1993 10:21:01 Raw View
In article <CEtHBL.H1L@actrix.gen.nz> hugp@swell.actrix.gen.nz (Peter Hug) writes:
>Sugestion 2:
>------------
>object type: My own class library provides a common root class (Object).
>This is not the case with most class libraries I have been exposed to. This
>often causes headaches. Assume my system is composed of objects belonging
>to independant class hierarchies. I now wish to implement a class with the
>purpose to notify other objects of certain events (e.g. a Timer class that
>notifies owners of timer-events). Objects of that class would have knowledge
>of the client and it's notifier member function. If I have a common root
>(i.e. just one class hierarchy exists) the owner is always a root type
>(e.g. an Object*) and everything works just fine.
Ok. Anyone, who had ever tried to implement a list of ints or floats
quickly using Borland s ClassLibrary w/o the templates, knows what you mean.
But I think it will be the wrong way to implement a new langauge struct for
it. Wouldn't it be better to gain some library standard (as it has become with
C standard libs) for C++ classes? For we are in a object oriented world, it
will only be nessecary to standardize the interface. Implementation details
would be up to the library provider.
Is there smth like this in the pipeline? I m using actually the gnu lib++,
which seems to be easier to use than Borland s.
Comments are welcome
Michael Luebbeke
fam-edv@zew.zew-mannheim.de <><><><><><><><><><><>
ZEW Zentrum fuer Europaeische Wirtschaftsforschung
Center of European Economic Research
Mannheim, Germany
===================================================
Author: hugp@swell.actrix.gen.nz (Peter Hug)
Date: Thu, 14 Oct 1993 00:10:13 GMT Raw View
In article <29g8f1$9sq@nic.lth.se>, Dag Bruck <dag@control.lth.se> wrote:
> Ancestor/super keyword.
>
> You can get almost all of the benefits of a new keyword by adding a a
> typedef to all your derived classes. For example:
>
> class A { ... }; // base class
>
> class B : public A {
> typedef A ancestor;
> public:
> // ...
> };
>
> Now you can write
>
> ancestor::f();
>
> in B to call A's f().
>
> When you change the class hierarchy, you also have to change the
> typedef, but it is a small inconvenience. Trick originally suggested
> by Mike Tiemann.
>
>
While this seems a good idea, one of the problems will be that it only
works on shallow class hierarchies. Take your example and lets say class A
descends from class X. The class A header may now declare:
typedef X ancestor;
Since the compiler requires knowledge of class A (a simple forward
declaration is insufficient), a declaration in class B's header like:
typedef A ancestor;
would fail since the type is already defined. Thus, this solution does not
Author: Kostya Vasilyev <Kostya@Symantec.com>
Date: 13 Oct 1993 16:22:50 GMT Raw View
In article <CEtHBL.H1L@actrix.gen.nz> Peter Hug, hugp@swell.actrix.gen.nz
writes:
>"super" or "ancestor" keyword: When early binding an ancestors method,
the
>ancestors class name must be supplied. When the hierarchy is altered or
>the ancestors class name changed, descendent classes may be affected. The
>implementation of a Smalltalk like super key word (or perhaps ancestor)
>would allow me to invoke the ancestors method via this keyword. Consider:
> [sample code deleted]
How about this (a well-known trick, actually):
class A { void Foo(); ... };
class B : public A
{
private:
typedef A inherited;
void Foo( inherited::Foo(); ... );
};
If you change the class from which B is derived, all you need to do is to
change the typedef. Of course, this is still more work than the compiler
doing this for you ;-) But you can use it now, as opposed to waiting for
compilers to implement it.
>object type: My own class library provides a common root class (Object).
>This is not the case with most class libraries I have been exposed to.
This
>often causes headaches. Assume my system is composed of objects belonging
>to independant class hierarchies. I now wish to implement a class with
the
>purpose to notify other objects of certain events (e.g. a Timer class
that
>notifies owners of timer-events). Objects of that class would have
knowledge
>of the client and it's notifier member function. If I have a common root
>(i.e. just one class hierarchy exists) the owner is always a root type
>(e.g. an Object*) and everything works just fine.
>
>However if we need to store a pointer to an object where it is not clear
>to which hierarchy the object belongs, we would have to store the pointer
>as a void pointer and would not be able to create a genericly applicable
>timer class for example. If we had an object pointer, we could do some-
>thing like:
> [sample code deleted]
How about using a template class?
// timer list: contains a list of timers
class CTimerList
{
public:
....
DoAll()
{ CTimerListIterator i(...);
while(i.Next())
i->NotifyClient();
}
};
// base class allows CTimerBase's to be placed into a list, and be used
// polymorphically
class CTimerBase
{
public:
CTimerBase(CTimerList& list)
{ list.Add(this); }
virtual void NotifyClient() = 0;
};
// now for the template class, which actually contains the client object
ptr;
// all classes specialized from this template inherit from TimerBase,
which
// means that they are polymorphic and can be added to the list.
template <class tClient>
class CTimer : private CTimerBase
{
public:
CTimer(CTimerList& list, tClient* client) :
CTimerBase(list),
fClient(client)
{ }
virtual void NotifyClient()
{ fClient->TimerEvent(); }
private:
tClient* fClient;
};
Usage:
class CCaret {
public:
CCaret(...) :
fTimer(gCaretTimerList, this)
{ ... }
void TimerEvent()
{ Blink(); }
private:
CTimer<CCaret> fTimer;
};
--------------------------------------------------------------------------
Kostya Vasilyev, SYMANTEC Corporation, Bedrock group
Cytomax Junkie 10201 Torre Avenue
Cupertino, CA 95014
(408) 446-7165
eMail: Kostya_Vasilyev_at_SYMCU-DEV@Symantec.com
--------------------------------------------------------------------------
Author: dag@control.lth.se (Dag Bruck)
Date: 14 Oct 1993 06:46:18 GMT Raw View
In <comp.std.c++> hugp@swell.actrix.gen.nz (Peter Hug) writes:
>In article <29g8f1$9sq@nic.lth.se>, Dag Bruck <dag@control.lth.se> wrote:
>>
>> class A { ... }; // base class
>>
>> class B : public A {
>> typedef A ancestor;
>> public:
>> // ...
>> };
>>
>While this seems a good idea, one of the problems will be that it only
>works on shallow class hierarchies. Take your example and lets say class A
>descends from class X. The class A header may now declare:
>
>typedef X ancestor;
>
>Since the compiler requires knowledge of class A (a simple forward
>declaration is insufficient), a declaration in class B's header like:
>
>typedef A ancestor;
>
>would fail since the type is already defined. Thus, this solution does not
Note that the typedef should be declared inside every derived class;
"ancestor" is a local type in each class, but may of course have
different meanings in different classes -- otherwise it wouldn't work.
-- Dag