Topic: Some features to be added to class-based OO languages


Author: o.v@k.ro (O.V.)
Date: Thu, 6 Feb 2003 18:25:00 +0000 (UTC)
Raw View
allan_w@my-dejanews.com (Allan W) wrote in message news:<7f2735a5.0302031617.31c35622@posting.google.com>...
> o.v@k.ro (O.V.) wrote
>
> > 1. Dynamic inheritance
> >
> > The major drawback of the classical inheritance scheme are the
> > limitations in combining different types (classes) on the same object.
> > Letting an object of class A be also of class B and C can be
> > difficult, especially when B and C are subclasses of A. This is not
> > easily accomplished even with multiple inheritance.
> > Dynamic inheritance solves this by allowing the base object of a
> > derived object to be chosen explicitly in the client function.
> >
> > Dynamic inheritance is provided in some languages, but it is said to
> > be difficult or even impossible to support in class-based languages.
> > It is also said to be unsafe, which may be true, but in my view the
> > benefits are substantial enough to be worth having it. As for the
> > implementation difficulties, I may be wrong but I really don't think
> > so. I will therefore give a brief description of what I have in mind;
> > it may be a somewhat limited version but provides in my opinion most
> > of the benefits of the full scheme. And it's doable; in fact the
> > interface-based approach can be implemented with some small effort in
> > today's C++.
>
> Would you please reword this? Assume that most of your audience is
> like me: almost all of my OO experience comes from C++, so any
> proposal to change the OO foundation in C++ is going to seem as if
> it was no longer OO, even if it really is more so. If you lived
> your whole life in the African desert, and then someone showed you
> an ice cube and said, "this is water," you would have trouble
> seeing it -- that's probably similar to why I can't understand your
> point here.

I'm not going to reword this because I think it expresses exactly what
it should. However, I confess I'm also like you, most of my OO
experience comes from C++, and I really like your analogy with the ice
cube.

> In particular, I've spent many years modeling data to fit well
> with the concept of C++'s classes. A Hyundai is-a car is-a vehicle
> is-a machine, and so on. It seems like you want to allow some
> functions to allow that a Hyundai is-a duck, even though that isn't
> true anywhere else in the program. In my experience it would make
> more sense to create a Hyundai_Duck class, either with multiple
> inheritance or with a contained duck. Obviously what you have in
> mind is something different; you want to (temporarily?) look at
> Hyundai and (without changing the data?) consider it as a type
> of duck. Is that close?

No. Think of it like this: if I have a Hyundai class and a Duck class,
I am able in a function to create an object that is a Hyundai and a
Duck. So that the common virtual methods call each other well. In
fact, I have to indicate more, in order that the methods call well: I
have to say which one is on top, the Hyundai or the Duck. Exactly one
of them must be on top, and a Hyundai on top of a Duck is usually
something quite different than a Duck on top of a Hyundai. But both
types are as well a Hyunday as they are a Duck, same way as a Hyundai
is still a car, even if it has something on top of it (the Hyundai
part).
Anyway, this in no way implies that a Hyundai is a Duck. If I want to
create classes for such objects, that is Hyundai's on top of Ducks or
Ducks on top of Hyundai's, I can, the way I explained in my posting.
This can be fairly well achieved with multiple inheritance or with
containining classes, as long as there are no common virtual methods
that must call each other well. If there are such methods, the best
approach imo is dyn. inheritance.

> I'd like to see some (hopefully simple) example of how this would
> be used and why it would be an advantage.

In my posting, I have given the best example I know. It may not be the
simplest possible one, but it's definitely not complicated, and it's
worth reading, I hope.

Anyway, thanks also for your interest.
O.V.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: o.v@k.ro (O.V.)
Date: Thu, 6 Feb 2003 18:25:49 +0000 (UTC)
Raw View
john@nospam.demon.co.uk (John G Harris) wrote in message news:<$LrUE6OgquN+EwOI@jgharris.demon.co.uk>...
> In article <59143289.0301271214.4aaeeb32@posting.google.com>, O.V.
> <o.v@k.ro> writes
>   <snip>
> >2.1. Seamless interface conversions
>  <snip>
> >an object
> >should be possible to use as an interface if it has all the methods of
> >that interface.
>   <snip>
>
> That's a very misleading assumption.

In general, yes. In this case, no.

> Remember the BS example : do you expect
>   void draw();
> to make a picture, fire a six-gun, hand out a poker card, get money out
> of a bank, or what?
>
> Inheritance provides a unique and unbreakable link from an object's
> semantics to an interface's semantics.

There is a simple and imo final answer to this: permit it at compile
time, don't permit it at run time.

Calling Draw is OK in

Painter painter;
painter.Draw ();

because the call is explicitly done for a Painter object; whereas it
would be wrong, if it were possible, in

void* p = ...; // some object
p->Draw ();    // call Draw at run time
               // if the pointed object has the method

because you don't know what kind of an object p points to, and hence
what its Draw method does, even if it has one.

Similarly, it is correct to convert the Painter above to an interface
IDraw with the method Draw on it, but it's wrong to convert the void*
p to that same interface, even if the pointed object has a Draw
method.
(Seems like a painter and a pointer are 2 very different things. (:-))
At run time, one should obtain the IPainter interface of the object,
if it provides it, then convert the IPainter pointer to the IDraw
interface, which is OK and which is done at complie time. If IPainter
is not provided by the object, the return pointer is NULL so the
caller can take appropriate action.

O.V.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: o.v@k.ro (O.V.)
Date: Thu, 6 Feb 2003 19:07:38 +0000 (UTC)
Raw View
poenitz@gmx.net (=?iso-8859-1?Q?Andr=E9_P=F6nitz?=) wrote in message news:<b18tg9$2p0$1@narses.hrz.tu-chemnitz.de>...
> O.V. <o.v@k.ro> wrote:
> > The major drawback of the classical inheritance scheme are the
> > limitations in combining different types (classes) on the same object.
> > Letting an object of class A be also of class B and C can be
> > difficult, especially when B and C are subclasses of A. This is not
> > easily accomplished even with multiple inheritance.
>
> Isn't that solvable by virtual inheritance?

No, it's not.
You can try to code my example of the tax payer classes with virtual
inheritance. Also, in adding functionality in one move to all classes
that derive from the same base class (such as all MFC classes derived
from TWindow), virtual inheritance doesn't help.
(In fact, the only uses I know of it are 1. minimising the memory
space required for the derived objects and 2. eliminating the need of
the immediate base class qualifier. Do you know of any other use ? (If
you do, it may be better you write it to me by e-mail, except if it
has any relevance for the subject of the thread.) Thanks.)

O.V.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: poenitz@gmx.net (=?iso-8859-1?Q?Andr=E9_P=F6nitz?=)
Date: Sat, 1 Feb 2003 00:29:33 +0000 (UTC)
Raw View
O.V. <o.v@k.ro> wrote:
> The major drawback of the classical inheritance scheme are the
> limitations in combining different types (classes) on the same object.
> Letting an object of class A be also of class B and C can be
> difficult, especially when B and C are subclasses of A. This is not
> easily accomplished even with multiple inheritance.

Isn't that solvable by virtual inheritance?

Andre'

--
Those who desire to give up Freedom in order to gain Security,
will not have, nor do they deserve, either one. (T. Jefferson)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: allan_w@my-dejanews.com (Allan W)
Date: Tue, 4 Feb 2003 06:43:26 +0000 (UTC)
Raw View
o.v@k.ro (O.V.) wrote

> 1. Dynamic inheritance
>
> The major drawback of the classical inheritance scheme are the
> limitations in combining different types (classes) on the same object.
> Letting an object of class A be also of class B and C can be
> difficult, especially when B and C are subclasses of A. This is not
> easily accomplished even with multiple inheritance.
> Dynamic inheritance solves this by allowing the base object of a
> derived object to be chosen explicitly in the client function.
>
> Dynamic inheritance is provided in some languages, but it is said to
> be difficult or even impossible to support in class-based languages.
> It is also said to be unsafe, which may be true, but in my view the
> benefits are substantial enough to be worth having it. As for the
> implementation difficulties, I may be wrong but I really don't think
> so. I will therefore give a brief description of what I have in mind;
> it may be a somewhat limited version but provides in my opinion most
> of the benefits of the full scheme. And it's doable; in fact the
> interface-based approach can be implemented with some small effort in
> today's C++.

Would you please reword this? Assume that most of your audience is
like me: almost all of my OO experience comes from C++, so any
proposal to change the OO foundation in C++ is going to seem as if
it was no longer OO, even if it really is more so. If you lived
your whole life in the African desert, and then someone showed you
an ice cube and said, "this is water," you would have trouble
seeing it -- that's probably similar to why I can't understand your
point here.

In particular, I've spent many years modeling data to fit well
with the concept of C++'s classes. A Hyundai is-a car is-a vehicle
is-a machine, and so on. It seems like you want to allow some
functions to allow that a Hyundai is-a duck, even though that isn't
true anywhere else in the program. In my experience it would make
more sense to create a Hyundai_Duck class, either with multiple
inheritance or with a contained duck. Obviously what you have in
mind is something different; you want to (temporarily?) look at
Hyundai and (without changing the data?) consider it as a type
of duck. Is that close?

I'd like to see some (hopefully simple) example of how this would
be used and why it would be an advantage.

For now, don't focus on HOW the compiler would do this, unless it's
neccesary in order to understand WHAT it is doing. Focus instead
please on what it means to "let an object of class A also be of
class B and C" in some way other than inheritance.

Thanks.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: o.v@k.ro (O.V.)
Date: Tue, 28 Jan 2003 18:51:44 +0000 (UTC)
Raw View
===================================== MODERATOR'S COMMENT:
 If posting replies, please do quote only the relevant pieces of
this message.



===================================== END OF MODERATOR'S COMMENT
The points below could be added to C++ and, as far as I know, also to
Java and C#, and probably most if not all of the other class-based
languages (whichever they are). I am going to present them in
connection to C++. Some of the details may not be completely accurate
with respect to other languages, but the basic ideas should be
immediately appliable to them also.


1. Dynamic inheritance

The major drawback of the classical inheritance scheme are the
limitations in combining different types (classes) on the same object.
Letting an object of class A be also of class B and C can be
difficult, especially when B and C are subclasses of A. This is not
easily accomplished even with multiple inheritance.
Dynamic inheritance solves this by allowing the base object of a
derived object to be chosen explicitly in the client function.

Dynamic inheritance is provided in some languages, but it is said to
be difficult or even impossible to support in class-based languages.
It is also said to be unsafe, which may be true, but in my view the
benefits are substantial enough to be worth having it. As for the
implementation difficulties, I may be wrong but I really don't think
so. I will therefore give a brief description of what I have in mind;
it may be a somewhat limited version but provides in my opinion most
of the benefits of the full scheme. And it's doable; in fact the
interface-based approach can be implemented with some small effort in
today's C++.

1.1. Interface inheritance

An inheritable interface must declare its methods as public, protected
and virtual. Because of these characteristics, inheritable interfaces
are no longer a special case of classes; in an actual C++
interface-class, all methods are public and virtual. A special
interface keyword would be needed. An interface can contain neither
private nor data members.
With interfaces like this, dynamically inheriting from them is not
much different than from classes. A class can be declared as deriving
from an interface. This actually means deriving, not implementing that
interface. In contrast with inheriting from classes, a class cannot
inherit from more than 1 interface; also, if it inherits from an
interface it cannot also inherit from classes.
A class that inherits from an interface may initialise that interface
in its constructors. In C++ syntax this is done by "constructing" the
base interface from an interface reference, such as in:

interface I { ... };
class A: public I
{
  public:
    A (I& rI) : I (rI) {}
};

Which of course means that the A object stores rI as the reference to
its base object. This is the most typical case.
The building of the derived object is done by explicitly constructing
it in the client program over an arbitrary base object. The only
condition is that the base object implements the base interface of the
top class. A special keyword is needed to indicate this, since
implementing an interface is different than inheriting from it. For
example:

class B: implements I
{
...
};

void Client (void)
{
    B b;
    A a (b);
}

Such a construct must have the effect that the newly created A object
inherits its behaviour from the B object; it must function as if class
A had been declared as deriving from class B instead of interface I.
Specifically, this means:
- A has all the methods that are declared on I; they are public,
protected and virtual according to their declaration on I, as long as
they haven't been redefined in A, provided the "public" keyword has
been used. Functions of I that are not redefined by A have their
definition provided by the base object.
- Calls to the base methods in the methods of A result in those of the
base object.
- The virtual methods of the base interface, be they public or
protected, become virtual methods of the resulting object and can be
redefined in the derived class. This means that if they are called
from internal methods of the base object, the redefinition in A is
called; also if they are called from outside the object as public
methods of the base object.
- Virtual methods may exist that are not on the base interface, but
are common to the top class and the base object, because they have
been defined independently in both. These may or may not have to be
replaced in the base object by those of the top class, depending on
the client's needs. There may be situations when such a method must be
replaced, and others when it must not be. A mechanism must exist for
the programmer to indicate his/her intention. One possibility would be
to provide for the construction of the derived object an interface
derived from the base interface of the top class, which additionally
contains all methods that must be redefined and used in the new
object. Thus, in the above exapmle, if the construction of a is made
like:

    A a ((I1&)b);

where I1 is derived from I and contains some additional methods that
may be public, protected and virtual, these methods must then also
exist for the new object. The virtual ones that are defined also in A
must be replaced in the base object by their A definition. Obviously,
b must also implement the interface I1.

No object may be used as the base of more than 1 object at the same
time. This must be ensured by the implementation, and probably the
easiest way to do it is via a run-time test.

A class that derives from another one that derives from an interface
derives itself from the same interface, by definition.

Additionally, an object could be built over another one even if its
class is not declared as deriving from an interface. This is same as
treating any base class, i.e. a class that is derived neither from
another class nor from an interface, as if it derived from the void
interface. This poses a small problem in that this has to be indicated
when the object is built, because the top class doesn't have a
constructor any more that initializes its base interface. A possible
solution is to define a void interface, either as a language keyword
or by allowing it to be defined by the programmers with any name they
choose, such as in

interface MyVoidInterface {};

and use it to the sole purpose of indicating that an object has to be
built on top of another one

C c ((MyVoidInterface&)d);

where C does not derive from any interface. The indication of methods
of D to be redefined and used in C could then be done like above, via
an interface that derives from the void one:

interface MethodsOfD : MyVoidInterface
{
...
};

C c ((MethodsOfD&)d);

An argument of this kind could be given as the final implicit argument
of any constructor of the top class. Thus, the constructor of C which
would be called above would be the default one.

Conversely, it could be possible that a class declared as derived from
an interface be used standalone, if it has one or more constructors
that don't initialise the base interface; this is then implicitly
initialised to NULL. Alternatively, constructors could be allowed or
even mandated to initialise the base interface to NULL if the object
is to be used standalone. For example:

interface I { ... };
class A: public I
{
  public:
    A (I& rI) : I (rI) {}
    A (void) : I (NULL) {}
};

or something like this. Maybe the better way would be to treat the
base interface like a pointer, since it can be NULL; then the I
argument constructor above could be

    A (I* pI) : I (pI) {}

or

    A (I& rI) : I (&rI) {}

In the methods of the class, the base interface could then be tested
for non-NULL, such as in

int A::ComputeSomething (char* p)
{
    int i = 0;
    if (I != NULL)
        i = I::ComputeSomething (p);

    ... // computation based on the value returned by the base class
if any
}

In classical dynamic inheritance, the base class / object may be
changed during the whole life time of an object, not just at its
creation. This could be permitted also with the above definition,
provided the new base object is of the initial common base class (BC
in the above examples) of the base and top object. Again I'm not sure
if this would be useful, that is bring enough benefits to compensate
for the potential dangers. My feeling is that it wouldn't be; most of
the benefits are achieved just by being able to construct objects
dynamically in this manner.

These are the basics. There are of course many more details which have
to be solved for implementing it, and may be solved differently by
different implementations.

The benefits of this feature should be obvious. I will point out some
of the most useful I know.

It is possible this way to enhance in a move all classes that derive
from a given base class. For instance, if one wanted to add some
functionality to all MFC windows, (s)he would write a class that
inherits from the interface of TWindow; this class could then redefine
virtual methods, call base methods etc.. Then the actual window
objects would be constructed as dynamically-inherited objects of this
class over any object of any class derived from TWindow.

Then, 2 or more classes which share a common base class could be
combined together. I'm going to give an example which will sound
didactical, but I couldn't think of a more practical situation, though
I'm sure they are out there somewhere.
Let's say we want to model the income-and-taxes situations of people.
In this respect, everybody is a citizen and must pay taxes from what
(s)he urns. There are many other classes people may be of; every one
(taken into consideration) has either a profession or is retired; some
more than one. They may also have tax-deductible expenses.
Now there may be certain classes of people for which the taxes are
computed differently than for ordinary people, such as the retired or
maybe the professors (this is just theory). Such a class need not be
an income class, for instance it might be a class of war heroes which
don't get money for this, but instead pay reduced taxes. Moreover, the
tax that the war hero has to pay may depend not on its income
directly, but on the tax that he would pay if he were not a war hero.
This way, the war heroes that are also retired would benefit from both
these cathegories they are in.
In the general case we are going to model, every tax cathegory
computes the tax as a function of 2 arguments: the total income of the
person and the tax the person would pay if (s)he were not of that tax
cathegory. This is the general scheme, some tax cathegories may
actually depend on just 1 of these 2 inputs. Of course, in order to
combine these cathegories, they must have relative priorities, so that
a war hero which is also retired can know that he must first compute
his tax according to the retired class, then adjust it by the formula
for war heroes.
Aside the tax cathegories there are income cathegories, which are
characterised by an income. Every class computes this income in its
own personal way, which may involve entering of input data, which may
lead to the situation that the income at a certain moment is undefined
etc.. These aspects are not important for our example, so we'll just
assume that every class can compute at any moment its income according
to its own function.
There are also cathegories that are income and tax cathegories at the
same time, such as the retired and the professors. There are many
cathegories of income only, and also some of tax only, such as the war
heroes. The ordinary citizen cathegory may be also viewed as a
tax-only one.
Any person taken into consideration will belong to any number of
classes of any type, but at least one will have to be of income (only
or mixed) and also at least one of taxes; if a person does not belong
to a special tax class then (s)he is of the citisen tax class.
Now with dyn. inheritance, this could be coded by having an interface
ICitizen, with the 2 virtual methods Income and Taxes. Every tax or
income cathegory would be coded as a class that derives from this
interface; it must implement the Income and / or the Taxes methods. A
person is then modelled by an object composed by several tax + income
objects on top of one another, one for every cathegory (s)he is in.
The base object for every person is a Citizen object, which only
computes the default taxes; this is true even if the person belongs to
one or more special tax classes, because these may require the taxes
that would be payed in their absence. The building order of these
objects is given by their priority; the Citizen object is first, then
the income-only objects in no matter what order, since they are of
equal (lowest) priority, then the tax (only or combined) objects by
their priorities, which must be different for any 2 tax classes.
The income classes compute their income as a total one, that is by
adding their own income to the one given by the base object, such as:

double Professor::Income (void)
{
    return ProfessorOwnIncome () + ICitizen::Income ();
}

The tax classes compute the taxes based on the total income, given by
the final redefined Income function, and / or on the preexisting
taxes, which they obtain as the Taxes method of the base object:

double WarHero::Taxes (void)
{
    return WarHeroTaxes (Income (), ICitizen::Taxes ());
    // or maybe just WarHeroTaxes (ICitizen::Taxes ())
    // depending on the taxes formula for war heroes
}

The Citizen class only computes the default taxes based on the total
income; to make things work, it must also have an Income method that
returns 0:

double Citizen::Income (void)
{
    return 0;
}

double Citizen::Taxes (void)
{
    return CitizenTaxes (Income ());
}

In the combined object, the final redefined Income method will give
the total income, and the final redefined Taxes method the taxes the
person has to pay.

(By the way, how does one compute taxes in Texas ? (:-))

As I said, I don't believe this is the best possible example, I'd
appreciate if somebody could provide me with a better one. But as far
as I know, for this problem as I put it, there is no better solution
than this one with dyn. inheritance; of course it can be solved
differently, but any other solution would be more complicated and less
maintainable. The natural solution is IMO through dynamic inheritance.

Another area of use would be component programming. The implementation
of base classes of objects could be changed same as it is possible in
COM for the implementation of the objects used in a program by their
public interfaces.
Finally one other improvement would be that a class wouldn't have to
be declared completely any more in order to be inherited from. The
private methods and fields of a class would then never be visible in
the client any more, just as it is for interface access, provided the
objects are always used by interface pointers, not as statically
allocated variables.

As an aside, interface inheritance is possible also in actual C++ or
C# or in component models like COM, thus making it to support
implementation inheritance. For COM, the basics are that in order to
be inheritable, the base COM object must define 2 interfaces: a
protected one and a virtual one. The protected interface must contain,
in addition to the 3 default COM methods, all object methods that can
be used in a derived class. Besides, there must be a special method
RedefineVirtuals, which is placed best as the 4th one of the protected
interface and is used to redefine the virtual methods of the base
object. This method takes a single argument of type IVirtual*, where
IVirtual is the virtual interface of the base object. It may not
contain the 3 COM default methods, but must contain all virtual
methods of the base object. All these methods must also be present on
the protected interface.
To derive from such an object, a class C must do the following:
- hold a pointer to the object's protected interface in a class field
- redefine all virtual methods of the base object
- create an interface with the redefined methods (the best way to do
this is to derive itself from IVirtual) and call RedefineVirtuals for
a pointer to this interface.
All methods of IVirtual must be redefined, even those that the class
doesn't actually change, or else the methods of the base object
couldn't be redefined any more in a class derived from C. For the
methods it doesn't want to change, a call to the corresponding method
on the protected interface of the COM object must be made in the
corresponding method of C. For the other ones, whenever a call to a
base method must be made, it is done by calling that method on the
protected interface of the base object.
The base COM object must be written such that after its virtual
methods have been redefined by a call to RedefineVirtuals, the methods
on the supplied interface are used instead of the original ones. This
pertains to the call of the virtual methods inside the COM object as
well as to the methods that are exposed on other (public) COM
interfaces. On the contrary, the methods on the protected interface
must remain the original ones after the redefinition, because they are
called from the methods of the top class as base object methods.
This is not very difficult to achieve by holding a pointer to the
supplied IVirtual inside the COM object, and calling its methods if it
is non-null, else the original ones. These must be separated from the
actual virtual methods of the object in order to place them on the
protected interface.
The derived class C obtained this way can be used like any other
class; its methods can be redefined in which case the redefinition
applies also inside the base COM object. It can also be exported as a
COM object, even an inheritable one.

In "normal" C++, one can build through a similar mechanism classes
that derive from interfaces and classes that can be derived from via
interfaces. But the problem is that they must be defined like this,
which today's classes are not; if one has their sources (s)he can
conceivably modify them to do this (with some risks), but if they are
in libraries there's not much (s)he can do. In fact, even then the
base class can be made to be interface-inheritable, by means of an
additional derived class to manage the link to the top object. But
there's no trick for convincing a class C derived from B to call a
base method other than that of B.

All this can be done, but it would ve of course much more convenient
if there were automatical support. A pretty good such support could be
written for COM and today's C++, but not as good as language support
could be.

1.2. Hierarchical multiple inheritance

One final use of interface inheritance, which deserves a point on its
own, is that it replaces multiple inheritance. Instead of deriving a
class A from B, C and D, one can build an A object on top of a B on
top of a C on top of a D, provided of course that A, B and C have been
properly defined as inheriting from interfaces. It is even better, by
allowing one to specify the order of preference in case of method
collision. On the other hand, having to build 3 or 4 nested objects
instead of just 1 multiply-inherited one may be anoying if it has to
be done too many times.
The solution is to redefine multiple inheritance so that it supports
the same hierarchy as dynamic inheritance. Specifically, the
definition of class C as inheriting from B1, B2, ... Bn could mean
that the base classes must be built one on top of the other, in a
given order, i.e. either forwards or backwards. The reverse order
seems more natural, because when declaring

class C: B1, B2, ... Bn
{
...
};

C is closest to B1.
The base classes may or may not be declared as deriving from
interfaces, which must fit together, i.e. in the above example, the
base interface of B<i> must be implemented by B<i + 1>. If a class
does not derive from an interface then it is treated as deriving from
the void interface, which by definition is implemented by any other
class.
Such a declaration should then mean that the virtual methods of C
redefine those of B1 which redefine those of B2 etc. It would be
executed as a multiple dynamic inheritance declaration, that is
construct first the object Bn, then Bn-1 on top of it, and so on up to
B1, then C. The only difference would be that the various fields would
(usually) be constructed in a contiguous memory object.
If the last base class derives from an interface, then the whole class
C does, from the same interface; else it is standalone.
The methods of B<i + 1> that must be redefined and used in B<i> are
indicated in the constructors of C, like in:

C (int i)
: B4 ((InterfaceUseB5&)B5),
  B3 (i + 1, (InterfaceUseB4&)B4)
{
...
}

Then, if one has a class A that derives from interface I and a class B
that implements I, and wants to write a class that is A derived from
B, all that's needed is writing a class C like this:

class C: public A, B
{}

This works if A and B have void constructors; if not, the constructors
must be supplied in that of C, like for instance:

class C: public A, B
{
  public:
    C (int i, long l)
    : B (l),
      A (B, l, i)
    {}
}

A probably very good approach is to declare any substantive derived
class as deriving from an interface, then put the pieces together by
means of multiply inherited classes which do nothing else than
assemble the individual classes, like above.

1.3. Dynamic class inheritance

While the interface inheritance approach brings most if not all
benefits of dyn. inheritance, it has a minor drawback. If one had for
instance class A derived from B derived from C derived from D in a
classical sense, and wanted to use the interface-based approach, he
would write A, B and C as deriving from interfaces instead. Then he
would write another 3 classes ABCD, BCD and CD as above, to define his
actual classes:

class CD: public C, D {}
class BCD: public B, CD {}
class ABCD: public A, BCD {}

in the most simple case.

Then, if he wanted to use many times class A on top of B, to put it on
top of any object of interface C, he would also declare a class AB for
this:

class AB: public A, B {}

and also for any combination of classes he'd want to use. If there had
to be all combinations, then in our case there would be 6 of them.
Writing them wouldn't be that hard, though there would be more if
there were more levels of inheritance for the classes; but using every
one of them when appropriate would be cumbersome.

To simplify this for the programmer, a way could be defined for
indicating that a derived object has to be constructed over a
preexisting object, such that this object replaces one of the base
classes of the top class. For instance, in the above example, the
classes would be written normally:

class D { ... };
class C: public D { ... };
class B: public C { ... };
class A: public B { ... };

then an object of class A could be constructed over an object with the
interface of class C by a syntax like

A a ((base C&)e);

using a special keyword base. e could be of a class E that derives
from C, or otherways implements the interface of C. In this case, the
D and C part of the A object wouldn't be created any more, that is no
memory allocated and no constructors called.
Basically this is it. Then this would have to be combined with
anything defined above, such as multiple hierarchical interitance and
indicating the methods of the base object to be redefined and used in
the top object. I'll spare the details, since this posting is already
a little long.


2. A few minor matters

2.1. Seamless interface conversions

Interface programming is very good because it allows for a high level
of generality. The problem with today's languages is that in order to
use an interface pointer to an object, one must first define the
object as being derived from that interface.
There is no theoretical reason why it should be like this; an object
should be possible to use as an interface if it has all the methods of
that interface. In fact even in C++ one can work around the problem by
defining a class which implements the given interface, by calling for
every method of the interface the corresponding method of the object,
which it stores a pointer to. The conversion from the object to the
interface can be done automatically by defining an appropriate
constructor in this auxiliary class. By creating a template class for
the interface, the job can be automatically done for every object that
fits to it. Finally, in languages like Java and C#, the template is
not even needed, because of the run-time type information. But still,
a class or a template or whatever must be done to derive from the
interface.
There should be no need for this. Any object should be automatically
converted to an interface if it has all the required methods.
Likewise, an interface should be converted to another if it has all
its methods. In this respect, an object behaves like its public
interface, so I will only describe interface conversions.
To convert interface I1 to I2, every function of I2 must have a
corresponding one in I1. The I1 method for one in I2 is found by the
same rules as calling a method from I1 of the name and argument types
of the one in I2. Likewise, the best fitting method is chosen; there
may be no corresponding method, or there may be ambiguity situations.
If this is the case for any I2 method, the conversion from I1 to I2
cannot be performed.
If I2 contains methods that have interface arguments, these must be
matched to the ones in the corresponding I1 methods. Resolving this
recursivity at compile time might result in an undesired level of
complexity; therefore, it may be best to stop at some recursivity
level, considering the conversion is OK, and do the actual choice of
the I1 method at run time, issuing an exception if this cannot be
done.
Note that it can happen that 2 methods of I2 be resolved to the same
method of I1; this is perfectly legal and correct.
When I1 is used in a function as I2 by calling its methods, it would
be best if the arguments of the calls were converted directly to those
of the called I1 method where possible. But this must be done at run
time, so it may be thought preferably to resolve all calls at compile
time thus converting the call arguments first to I2 types then to I1
types.

To achieve an even greater level of generality, an interface should be
converted to another even when the names of the corresponding methods
differ; the argument types of course must match one another. This is
probably done best by means of a special type of operator, one that
tells the compiler what the corresponding I1 method is for every one
in I2. An approximate C++ syntax could be as follows:

operator I2 (I1&)
{
    I2::f1 (int, char*) = I1::f3 (long, char*, int);
                                     // (the int argument is optional)
    ....
}

These would be the only instructions allowed in such an operator. The
methods of I2 which are not assigned I1 ones are resolved the usual
way.
It could then be useful to also have named conversions from one
interface to another, specified in a similar way by declaring them as
functions instead of operators:

I2 I1ToI2Conversion1 (I1&)
{
    I2::f1 (int, char*) = I1::f3 (long, char*, int);
                                     // (the int argument is optional)
    ....
}

Such an explicit conversion would be written as a call to its
conversion function. Implicit conversions would be handled by the
conversion operator if one exists, else the default way.

Conversions among inheritable interfaces keep the public and virtual
attributes of the methods; i.e. virtual methods are always translated
to virtual ones and non-virtual methods to non-virtual ones; a
protected method cannot become public, but a public method can become
protected. This applies in particular to objects converted to
interfaces; thus, no protected object method can become public on the
interface.

This way of converting interfaces is a generalisation of .NET
delegates.

A signifficant issue for all points so far involves compile-time vs.
run-time execution. For instance, converting an interface to another
one like above could be done at run time, if the information is not
available at compile time, such as if the converted object is passed
as a void *. This would be possible or not, depending on the run-time
type information available in the language.
A drawback of all this would be that the methods of an object would be
too easily accessible, that is they could be used by mistake in places
where they fit structurally but not semantically. Having these
conversions performed automatically would presumably make such errors
easier. Therefore, a good balance may be to allow seamless interface
conversions only at compile time, for objects of known class. The idea
is that if a class is known completely, the users are supposed to be
allowed to do whatever they want to with its public members, on their
own risk. On the contrary, access by interface pointers implies a
semantical indication from the user, by having to request a method
only in connection with a specific interface. It would be against this
principle to enable free calling of any methods of such an object.
(By the same principle, it is kind of wrong to permit dynamic casting.
But in practice, if one wants his/her class to be used only by
interface pointers, (s)he doesn't make the class's header available to
users.)

If seamless interface conversions are provided completely, there is no
more need for a separate concept of implementing an interface; any
object which can be converted to an interface implements that
interface. If they are provided only at compile time, as above, then
implementing an interface could mean that it is available also at run
time.

2.2. Compile-time execution of constant function expressions

This is not directly pertaining to OO languages, rather to procedural
ones.
A constant function is one that depends only on the values of its
arguments; that is it does not interact in any way with global
variables (or class variables, for class methods) or with program
input (file or UI I/O), neither directly nor indirecty through called
functions. If such a function is called for arguments which are
constants (at least numeric types, NULL pointer values, possibly also
structures), the call is a constant function expression. The value of
such an expression can be computed at compile (or link) time, and
should be, because it is a very good optimisation. Besides, such an
expression should be considered a constant expression and used as
such, for instance allowed as the size of an array.
A trivial example, the factorial function

Fact (i) = i! = 1 * 2 * 3 * ... * i

written recursively in the obvious manner, gives a constant function
expression when used as

long l = Fact (10) + 1680302423019;

The initialisation value should be computed at compile time. Also,
constructs like

int iArray[2 * Fact (4)];

should be possible.
There may be some limitations with this I'm not aware of. A limited
version is anyway possible, at least it could be done for functions
that are defined entirely in class headers that are available at the
place of the call. Then, if I wanted to make use of this feature, I'd
define the factorial and other such functions as static class methods
in a class header. But it seems that by resolving them at link time,
all or most situations could be done for statically linked functions.

There is a way of obtaining a similar thing with templates, but it is
limited and has drawbacks that disqualify it for a practical solution:
- they are difficult to read and to debug
- the templated functions can be used only with constant arguments, so
that the functions would have to be duplicated, one for constant
arguments one for variable ones
- execution is very slow, because it is done by textual replacement

2.3 Named function arguments

This may seem very minor, besides it is again not directly OO-linked.
I think it would help if optional function arguments could be
indicated in calls by name instead of by position. This way one could
supply some of the implicit arguments, skipping the ones before them.

2.4. Replace references with pointers

This is the only point related to C++ specifically, and it is not an
addition but a deletion.
In order to reduce the array / pointer / reference redundancy, which
has become more of a matter since Java / C#, I think references could
be eliminated and replaced with pointers. Their use is covered by that
of pointers, except in copy constructors and assignment operators; so
let these be defined with pointers instead. As another possibility,
assignments such as:

int i, * p;
p = i;

could become legal and mean that p is assigned the address of i, like
for a reference.
I imagine this would break tons of legacy code, and I don't think
replacement would be done very easily (though it shouldn't be very
hard either), so this is IMO one situation where a simplification
would be good but costly in this respect.


Implementation of these features may prove difficult but seems
possible; I won't go into the details here but I thought it over to
some extent and there seem to be no undefeatable problems. Some of
them may require performance overheads, in memory as well as compile
and execution time.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: john@nospam.demon.co.uk (John G Harris)
Date: Tue, 28 Jan 2003 22:17:51 +0000 (UTC)
Raw View
In article <59143289.0301271214.4aaeeb32@posting.google.com>, O.V.
<o.v@k.ro> writes
  <snip>
>2.1. Seamless interface conversions
  <snip>
>an object
>should be possible to use as an interface if it has all the methods of
>that interface.
  <snip>

That's a very misleading assumption. Remember the BS example : do you
expect
  void draw();
to make a picture, fire a six-gun, hand out a poker card, get money out
of a bank, or what?

Inheritance provides a unique and unbreakable link from an object's
semantics to an interface's semantics.

  John
--
John Harris
  mailto:john@jgharris.demon.co.uk

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: o.v@k.ro (O.V.)
Date: Mon, 27 Jan 2003 20:33:19 +0000 (UTC)
Raw View
The points below could be added to C++ and, as far as I know, also to
Java and C#, and probably most if not all of the other class-based
languages (whichever they are). I am going to present them in
connection to C++. Some of the details may not be completely accurate
with respect to other languages, but the basic ideas should be
immediately appliable to them also.


- RTTI (de vazut ce planuri au); DE VAZUT NEAPARAT IN C# CUM E
- evt. probat var. cu template-uri ptr. factorial


1. Dynamic inheritance

The major drawback of the classical inheritance scheme are the
limitations in combining different types (classes) on the same object.
Letting an object of class A be also of class B and C can be
difficult, especially when B and C are subclasses of A. This is not
easily accomplished even with multiple inheritance.
Dynamic inheritance solves this by allowing the base object of a
derived object to be chosen explicitly in the client function.

Dynamic inheritance is provided in some languages, but it is said to
be difficult or even impossible to support in class-based languages.
It is also said to be unsafe, which may be true, but in my view the
benefits are substantial enough to be worth having it. As for the
implementation difficulties, I may be wrong but I really don't think
so. I will therefore give a brief description of what I have in mind;
it may be a somewhat limited version but provides in my opinion most
of the benefits of the full scheme. And it's doable; in fact the
interface-based approach can be implemented with some small effort in
today's C++.

1.1. Interface inheritance

An inheritable interface must declare its methods as public, protected
and virtual. Because of these characteristics, inheritable interfaces
are no longer a special case of classes; in an actual C++
interface-class, all methods are public and virtual. A special
interface keyword would be needed. An interface can contain neither
private nor data members.
With interfaces like this, dynamically inheriting from them is not
much different than from classes. A class can be declared as deriving
from an interface. This actually means deriving, not implementing that
interface. In contrast with inheriting from classes, a class cannot
inherit from more than 1 interface; also, if it inherits from an
interface it cannot also inherit from classes.
A class that inherits from an interface may initialise that interface
in its constructors. In C++ syntax this is done by "constructing" the
base interface from an interface reference, such as in:

interface I { ... };
class A: public I
{
  public:
    A (I& rI) : I (rI) {}
};

Which of course means that the A object stores rI as the reference to
its base object. This is the most typical case.
The building of the derived object is done by explicitly constructing
it in the client program over an arbitrary base object. The only
condition is that the base object implements the base interface of the
top class. A special keyword is needed to indicate this, since
implementing an interface is different than inheriting from it. For
example:

class B: implements I
{
...
};

void Client (void)
{
    B b;
    A a (b);
}

Such a construct must have the effect that the newly created A object
inherits its behaviour from the B object; it must function as if class
A had been declared as deriving from class B instead of interface I.
Specifically, this means:
- A has all the methods that are declared on I; they are public,
protected and virtual according to their declaration on I, as long as
they haven't been redefined in A, provided the "public" keyword has
been used. Functions of I that are not redefined by A have their
definition provided by the base object.
- Calls to the base methods in the methods of A result in those of the
base object.
- The virtual methods of the base interface, be they public or
protected, become virtual methods of the resulting object and can be
redefined in the derived class. This means that if they are called
from internal methods of the base object, the redefinition in A is
called; also if they are called from outside the object as public
methods of the base object.
- Virtual methods may exist that are not on the base interface, but
are common to the top class and the base object, because they have
been defined independently in both. These may or may not have to be
replaced in the base object by those of the top class, depending on
the client's needs. There may be situations when such a method must be
replaced, and others when it must not be. A mechanism must exist for
the programmer to indicate his/her intention. One possibility would be
to provide for the construction of the derived object an interface
derived from the base interface of the top class, which additionally
contains all methods that must be redefined and used in the new
object. Thus, in the above exapmle, if the construction of a is made
like:

    A a ((I1&)b);

where I1 is derived from I and contains some additional methods that
may be public, protected and virtual, these methods must then also
exist for the new object. The virtual ones that are defined also in A
must be replaced in the base object by their A definition. Obviously,
b must also implement the interface I1.

No object may be used as the base of more than 1 object at the same
time. This must be ensured by the implementation, and probably the
easiest way to do it is via a run-time test.

A class that derives from another one that derives from an interface
derives itself from the same interface, by definition.

Additionally, an object could be built over another one even if its
class is not declared as deriving from an interface. This is same as
treating any base class, i.e. a class that is derived neither from
another class nor from an interface, as if it derived from the void
interface. This poses a small problem in that this has to be indicated
when the object is built, because the top class doesn't have a
constructor any more that initializes its base interface. A possible
solution is to define a void interface, either as a language keyword
or by allowing it to be defined by the programmers with any name they
choose, such as in

interface MyVoidInterface {};

and use it to the sole purpose of indicating that an object has to be
built on top of another one

C c ((MyVoidInterface&)d);

where C does not derive from any interface. The indication of methods
of D to be redefined and used in C could then be done like above, via
an interface that derives from the void one:

interface MethodsOfD : MyVoidInterface
{
...
};

C c ((MethodsOfD&)d);

An argument of this kind could be given as the final implicit argument
of any constructor of the top class. Thus, the constructor of C which
would be called above would be the default one.

Conversely, it could be possible that a class declared as derived from
an interface be used standalone, if it has one or more constructors
that don't initialise the base interface; this is then implicitly
initialised to NULL. Alternatively, constructors could be allowed or
even mandated to initialise the base interface to NULL if the object
is to be used standalone. For example:

interface I { ... };
class A: public I
{
  public:
    A (I& rI) : I (rI) {}
    A (void) : I (NULL) {}
};

or something like this. Maybe the better way would be to treat the
base interface like a pointer, since it can be NULL; then the I
argument constructor above could be

    A (I* pI) : I (pI) {}

or

    A (I& rI) : I (&rI) {}

In the methods of the class, the base interface could then be tested
for non-NULL, such as in

int A::ComputeSomething (char* p)
{
    int i = 0;
    if (I != NULL)
        i = I::ComputeSomething (p);

    ... // computation based on the value returned by the base class
if any
}

In classical dynamic inheritance, the base class / object may be
changed during the whole life time of an object, not just at its
creation. This could be permitted also with the above definition,
provided the new base object is of the initial common base class (BC
in the above examples) of the base and top object. Again I'm not sure
if this would be useful, that is bring enough benefits to compensate
for the potential dangers. My feeling is that it wouldn't be; most of
the benefits are achieved just by being able to construct objects
dynamically in this manner.

These are the basics. There are of course many more details which have
to be solved for implementing it, and may be solved differently by
different implementations.

The benefits of this feature should be obvious. I will point out some
of the most useful I know.

It is possible this way to enhance in a move all classes that derive
from a given base class. For instance, if one wanted to add some
functionality to all MFC windows, (s)he would write a class that
inherits from the interface of TWindow; this class could then redefine
virtual methods, call base methods etc.. Then the actual window
objects would be constructed as dynamically-inherited objects of this
class over any object of any class derived from TWindow.

Then, 2 or more classes which share a common base class could be
combined together. I'm going to give an example which will sound
didactical, but I couldn't think of a more practical situation, though
I'm sure they are out there somewhere.
Let's say we want to model the income-and-taxes situations of people.
In this respect, everybody is a citizen and must pay taxes from what
(s)he urns. There are many other classes people may be of; every one
(taken into consideration) has either a profession or is retired; some
more than one. They may also have tax-deductible expenses.
Now there may be certain classes of people for which the taxes are
computed differently than for ordinary people, such as the retired or
maybe the professors (this is just theory). Such a class need not be
an income class, for instance it might be a class of war heroes which
don't get money for this, but instead pay reduced taxes. Moreover, the
tax that the war hero has to pay may depend not on its income
directly, but on the tax that he would pay if he were not a war hero.
This way, the war heroes that are also retired would benefit from both
these cathegories they are in.
In the general case we are going to model, every tax cathegory
computes the tax as a function of 2 arguments: the total income of the
person and the tax the person would pay if (s)he were not of that tax
cathegory. This is the general scheme, some tax cathegories may
actually depend on just 1 of these 2 inputs. Of course, in order to
combine these cathegories, they must have relative priorities, so that
a war hero which is also retired can know that he must first compute
his tax according to the retired class, then adjust it by the formula
for war heroes.
Aside the tax cathegories there are income cathegories, which are
characterised by an income. Every class computes this income in its
own personal way, which may involve entering of input data, which may
lead to the situation that the income at a certain moment is undefined
etc.. These aspects are not important for our example, so we'll just
assume that every class can compute at any moment its income according
to its own function.
There are also cathegories that are income and tax cathegories at the
same time, such as the retired and the professors. There are many
cathegories of income only, and also some of tax only, such as the war
heroes. The ordinary citizen cathegory may be also viewed as a
tax-only one.
Any person taken into consideration will belong to any number of
classes of any type, but at least one will have to be of income (only
or mixed) and also at least one of taxes; if a person does not belong
to a special tax class then (s)he is of the citisen tax class.
Now with dyn. inheritance, this could be coded by having an interface
ICitizen, with the 2 virtual methods Income and Taxes. Every tax or
income cathegory would be coded as a class that derives from this
interface; it must implement the Income and / or the Taxes methods. A
person is then modelled by an object composed by several tax + income
objects on top of one another, one for every cathegory (s)he is in.
The base object for every person is a Citizen object, which only
computes the default taxes; this is true even if the person belongs to
one or more special tax classes, because these may require the taxes
that would be payed in their absence. The building order of these
objects is given by their priority; the Citizen object is first, then
the income-only objects in no matter what order, since they are of
equal (lowest) priority, then the tax (only or combined) objects by
their priorities, which must be different for any 2 tax classes.
The income classes compute their income as a total one, that is by
adding their own income to the one given by the base object, such as:

double Professor::Income (void)
{
    return ProfessorOwnIncome () + ICitizen::Income ();
}

The tax classes compute the taxes based on the total income, given by
the final redefined Income function, and / or on the preexisting
taxes, which they obtain as the Taxes method of the base object:

double WarHero::Taxes (void)
{
    return WarHeroTaxes (Income (), ICitizen::Taxes ());
    // or maybe just WarHeroTaxes (ICitizen::Taxes ())
    // depending on the taxes formula for war heroes
}

The Citizen class only computes the default taxes based on the total
income; to make things work, it must also have an Income method that
returns 0:

double Citizen::Income (void)
{
    return 0;
}

double Citizen::Taxes (void)
{
    return CitizenTaxes (Income ());
}

In the combined object, the final redefined Income method will give
the total income, and the final redefined Taxes method the taxes the
person has to pay.

(By the way, how does one compute taxes in Texas ? (:-))

As I said, I don't believe this is the best possible example, I'd
appreciate if somebody could provide me with a better one. But as far
as I know, for this problem as I put it, there is no better solution
than this one with dyn. inheritance; of course it can be solved
differently, but any other solution would be more complicated and less
maintainable. The natural solution is IMO through dynamic inheritance.

Another area of use would be component programming. The implementation
of base classes of objects could be changed same as it is possible in
COM for the implementation of the objects used in a program by their
public interfaces.
Finally one other improvement would be that a class wouldn't have to
be declared completely any more in order to be inherited from. The
private methods and fields of a class would then never be visible in
the client any more, just as it is for interface access, provided the
objects are always used by interface pointers, not as statically
allocated variables.

As an aside, interface inheritance is possible also in actual C++ or
C# or in component models like COM, thus making it to support
implementation inheritance. For COM, the basics are that in order to
be inheritable, the base COM object must define 2 interfaces: a
protected one and a virtual one. The protected interface must contain,
in addition to the 3 default COM methods, all object methods that can
be used in a derived class. Besides, there must be a special method
RedefineVirtuals, which is placed best as the 4th one of the protected
interface and is used to redefine the virtual methods of the base
object. This method takes a single argument of type IVirtual*, where
IVirtual is the virtual interface of the base object. It may not
contain the 3 COM default methods, but must contain all virtual
methods of the base object. All these methods must also be present on
the protected interface.
To derive from such an object, a class C must do the following:
- hold a pointer to the object's protected interface in a class field
- redefine all virtual methods of the base object
- create an interface with the redefined methods (the best way to do
this is to derive itself from IVirtual) and call RedefineVirtuals for
a pointer to this interface.
All methods of IVirtual must be redefined, even those that the class
doesn't actually change, or else the methods of the base object
couldn't be redefined any more in a class derived from C. For the
methods it doesn't want to change, a call to the corresponding method
on the protected interface of the COM object must be made in the
corresponding method of C. For the other ones, whenever a call to a
base method must be made, it is done by calling that method on the
protected interface of the base object.
The base COM object must be written such that after its virtual
methods have been redefined by a call to RedefineVirtuals, the methods
on the supplied interface are used instead of the original ones. This
pertains to the call of the virtual methods inside the COM object as
well as to the methods that are exposed on other (public) COM
interfaces. On the contrary, the methods on the protected interface
must remain the original ones after the redefinition, because they are
called from the methods of the top class as base object methods.
This is not very difficult to achieve by holding a pointer to the
supplied IVirtual inside the COM object, and calling its methods if it
is non-null, else the original ones. These must be separated from the
actual virtual methods of the object in order to place them on the
protected interface.
The derived class C obtained this way can be used like any other
class; its methods can be redefined in which case the redefinition
applies also inside the base COM object. It can also be exported as a
COM object, even an inheritable one.

In "normal" C++, one can build through a similar mechanism classes
that derive from interfaces and classes that can be derived from via
interfaces. But the problem is that they must be defined like this,
which today's classes are not; if one has their sources (s)he can
conceivably modify them to do this (with some risks), but if they are
in libraries there's not much (s)he can do. In fact, even then the
base class can be made to be interface-inheritable, by means of an
additional derived class to manage the link to the top object. But
there's no trick for convincing a class C derived from B to call a
base method other than that of B.

All this can be done, but it would ve of course much more convenient
if there were automatical support. A pretty good such support could be
written for COM and today's C++, but not as good as language support
could be.

1.2. Hierarchical multiple inheritance

One final use of interface inheritance, which deserves a point on its
own, is that it replaces multiple inheritance. Instead of deriving a
class A from B, C and D, one can build an A object on top of a B on
top of a C on top of a D, provided of course that A, B and C have been
properly defined as inheriting from interfaces. It is even better, by
allowing one to specify the order of preference in case of method
collision. On the other hand, having to build 3 or 4 nested objects
instead of just 1 multiply-inherited one may be anoying if it has to
be done too many times.
The solution is to redefine multiple inheritance so that it supports
the same hierarchy as dynamic inheritance. Specifically, the
definition of class C as inheriting from B1, B2, ... Bn could mean
that the base classes must be built one on top of the other, in a
given order, i.e. either forwards or backwards. The reverse order
seems more natural, because when declaring

class C: B1, B2, ... Bn
{
...
};

C is closest to B1.
The base classes may or may not be declared as deriving from
interfaces, which must fit together, i.e. in the above example, the
base interface of B<i> must be implemented by B<i + 1>. If a class
does not derive from an interface then it is treated as deriving from
the void interface, which by definition is implemented by any other
class.
Such a declaration should then mean that the virtual methods of C
redefine those of B1 which redefine those of B2 etc. It would be
executed as a multiple dynamic inheritance declaration, that is
construct first the object Bn, then Bn-1 on top of it, and so on up to
B1, then C. The only difference would be that the various fields would
(usually) be constructed in a contiguous memory object.
If the last base class derives from an interface, then the whole class
C does, from the same interface; else it is standalone.
The methods of B<i + 1> that must be redefined and used in B<i> are
indicated in the constructors of C, like in:

C (int i)
: B4 ((InterfaceUseB5&)B5),
  B3 (i + 1, (InterfaceUseB4&)B4)
{
...
}

Then, if one has a class A that derives from interface I and a class B
that implements I, and wants to write a class that is A derived from
B, all that's needed is writing a class C like this:

class C: public A, B
{}

This works if A and B have void constructors; if not, the constructors
must be supplied in that of C, like for instance:

class C: public A, B
{
  public:
    C (int i, long l)
    : B (l),
      A (B, l, i)
    {}
}

A probably very good approach is to declare any substantive derived
class as deriving from an interface, then put the pieces together by
means of multiply inherited classes which do nothing else than
assemble the individual classes, like above.

1.3. Dynamic class inheritance

While the interface inheritance approach brings most if not all
benefits of dyn. inheritance, it has a minor drawback. If one had for
instance class A derived from B derived from C derived from D in a
classical sense, and wanted to use the interface-based approach, he
would write A, B and C as deriving from interfaces instead. Then he
would write another 3 classes ABCD, BCD and CD as above, to define his
actual classes:

class CD: public C, D {}
class BCD: public B, CD {}
class ABCD: public A, BCD {}

in the most simple case.

Then, if he wanted to use many times class A on top of B, to put it on
top of any object of interface C, he would also declare a class AB for
this:

class AB: public A, B {}

and also for any combination of classes he'd want to use. If there had
to be all combinations, then in our case there would be 6 of them.
Writing them wouldn't be that hard, though there would be more if
there were more levels of inheritance for the classes; but using every
one of them when appropriate would be cumbersome.

To simplify this for the programmer, a way could be defined for
indicating that a derived object has to be constructed over a
preexisting object, such that this object replaces one of the base
classes of the top class. For instance, in the above example, the
classes would be written normally:

class D { ... };
class C: public D { ... };
class B: public C { ... };
class A: public B { ... };

then an object of class A could be constructed over an object with the
interface of class C by a syntax like

A a ((base C&)e);

using a special keyword base. e could be of a class E that derives
from C, or otherways implements the interface of C. In this case, the
D and C part of the A object wouldn't be created any more, that is no
memory allocated and no constructors called.
Basically this is it. Then this would have to be combined with
anything defined above, such as multiple hierarchical interitance and
indicating the methods of the base object to be redefined and used in
the top object. I'll spare the details, since this posting is already
a little long.


2. A few minor matters

2.1. Seamless interface conversions

Interface programming is very good because it allows for a high level
of generality. The problem with today's languages is that in order to
use an interface pointer to an object, one must first define the
object as being derived from that interface.
There is no theoretical reason why it should be like this; an object
should be possible to use as an interface if it has all the methods of
that interface. In fact even in C++ one can work around the problem by
defining a class which implements the given interface, by calling for
every method of the interface the corresponding method of the object,
which it stores a pointer to. The conversion from the object to the
interface can be done automatically by defining an appropriate
constructor in this auxiliary class. By creating a template class for
the interface, the job can be automatically done for every object that
fits to it. Finally, in languages like Java and C#, the template is
not even needed, because of the run-time type information. But still,
a class or a template or whatever must be done to derive from the
interface.
There should be no need for this. Any object should be automatically
converted to an interface if it has all the required methods.
Likewise, an interface should be converted to another if it has all
its methods. In this respect, an object behaves like its public
interface, so I will only describe interface conversions.
To convert interface I1 to I2, every function of I2 must have a
corresponding one in I1. The I1 method for one in I2 is found by the
same rules as calling a method from I1 of the name and argument types
of the one in I2. Likewise, the best fitting method is chosen; there
may be no corresponding method, or there may be ambiguity situations.
If this is the case for any I2 method, the conversion from I1 to I2
cannot be performed.
If I2 contains methods that have interface arguments, these must be
matched to the ones in the corresponding I1 methods. Resolving this
recursivity at compile time might result in an undesired level of
complexity; therefore, it may be best to stop at some recursivity
level, considering the conversion is OK, and do the actual choice of
the I1 method at run time, issuing an exception if this cannot be
done.
Note that it can happen that 2 methods of I2 be resolved to the same
method of I1; this is perfectly legal and correct.
When I1 is used in a function as I2 by calling its methods, it would
be best if the arguments of the calls were converted directly to those
of the called I1 method where possible. But this must be done at run
time, so it may be thought preferably to resolve all calls at compile
time thus converting the call arguments first to I2 types then to I1
types.

To achieve an even greater level of generality, an interface should be
converted to another even when the names of the corresponding methods
differ; the argument types of course must match one another. This is
probably done best by means of a special type of operator, one that
tells the compiler what the corresponding I1 method is for every one
in I2. An approximate C++ syntax could be as follows:

operator I2 (I1&)
{
    I2::f1 (int, char*) = I1::f3 (long, char*, int);
                                     // (the int argument is optional)
    ....
}

These would be the only instructions allowed in such an operator. The
methods of I2 which are not assigned I1 ones are resolved the usual
way.
It could then be useful to also have named conversions from one
interface to another, specified in a similar way by declaring them as
functions instead of operators:

I2 I1ToI2Conversion1 (I1&)
{
    I2::f1 (int, char*) = I1::f3 (long, char*, int);
                                     // (the int argument is optional)
    ....
}

Such an explicit conversion would be written as a call to its
conversion function. Implicit conversions would be handled by the
conversion operator if one exists, else the default way.

Conversions among inheritable interfaces keep the public and virtual
attributes of the methods; i.e. virtual methods are always translated
to virtual ones and non-virtual methods to non-virtual ones; a
protected method cannot become public, but a public method can become
protected. This applies in particular to objects converted to
interfaces; thus, no protected object method can become public on the
interface.

This way of converting interfaces is a generalisation of .NET
delegates.

A signifficant issue for all points so far involves compile-time vs.
run-time execution. For instance, converting an interface to another
one like above could be done at run time, if the information is not
available at compile time, such as if the converted object is passed
as a void *. This would be possible or not, depending on the run-time
type information available in the language.
A drawback of all this would be that the methods of an object would be
too easily accessible, that is they could be used by mistake in places
where they fit structurally but not semantically. Having these
conversions performed automatically would presumably make such errors
easier. Therefore, a good balance may be to allow seamless interface
conversions only at compile time, for objects of known class. The idea
is that if a class is known completely, the users are supposed to be
allowed to do whatever they want to with its public members, on their
own risk. On the contrary, access by interface pointers implies a
semantical indication from the user, by having to request a method
only in connection with a specific interface. It would be against this
principle to enable free calling of any methods of such an object.
(By the same principle, it is kind of wrong to permit dynamic casting.
But in practice, if one wants his/her class to be used only by
interface pointers, (s)he doesn't make the class's header available to
users.)

If seamless interface conversions are provided completely, there is no
more need for a separate concept of implementing an interface; any
object which can be converted to an interface implements that
interface. If they are provided only at compile time, as above, then
implementing an interface could mean that it is available also at run
time.

2.2. Compile-time execution of constant function expressions

This is not directly pertaining to OO languages, rather to procedural
ones.
A constant function is one that depends only on the values of its
arguments; that is it does not interact in any way with global
variables (or class variables, for class methods) or with program
input (file or UI I/O), neither directly nor indirecty through called
functions. If such a function is called for arguments which are
constants (at least numeric types, NULL pointer values, possibly also
structures), the call is a constant function expression. The value of
such an expression can be computed at compile (or link) time, and
should be, because it is a very good optimisation. Besides, such an
expression should be considered a constant expression and used as
such, for instance allowed as the size of an array.
A trivial example, the factorial function

Fact (i) = i! = 1 * 2 * 3 * ... * i

written recursively in the obvious manner, gives a constant function
expression when used as

long l = Fact (10) + 1680302423019;

The initialisation value should be computed at compile time. Also,
constructs like

int iArray[2 * Fact (4)];

should be possible.
There may be some limitations with this I'm not aware of. A limited
version is anyway possible, at least it could be done for functions
that are defined entirely in class headers that are available at the
place of the call. Then, if I wanted to make use of this feature, I'd
define the factorial and other such functions as static class methods
in a class header. But it seems that by resolving them at link time,
all or most situations could be done for statically linked functions.

There is a way of obtaining a similar thing with templates, but it is
limited and has drawbacks that disqualify it for a practical solution:
- they are difficult to read and to debug
- the templated functions can be used only with constant arguments, so
that the functions would have to be duplicated, one for constant
arguments one for variable ones
- execution is very slow, because it is done by textual replacement

2.3 Named function arguments

This may seem very minor, besides it is again not directly OO-linked.
I think it would help if optional function arguments could be
indicated in calls by name instead of by position. This way one could
supply some of the implicit arguments, skipping the ones before them.

2.4. Replace references with pointers

This is the only point related to C++ specifically, and it is not an
addition but a deletion.
In order to reduce the array / pointer / reference redundancy, which
has become more of a matter since Java / C#, I think references could
be eliminated and replaced with pointers. Their use is covered by that
of pointers, except in copy constructors and assignment operators; so
let these be defined with pointers instead. As another possibility,
assignments such as:

int i, * p;
p = i;

could become legal and mean that p is assigned the address of i, like
for a reference.
I imagine this would break tons of legacy code, and I don't think
replacement would be done very easily (though it shouldn't be very
hard either), so this is IMO one situation where a simplification
would be good but costly in this respect.


Implementation of these features may prove difficult but seems
possible; I won't go into the details here but I thought it over to
some extent and there seem to be no undefeatable problems. Some of
them may require performance overheads, in memory as well as compile
and execution time.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]