From 402189877181230481
X-Google-Language: ENGLISH,ASCII-7-bit
X-Google-Thread: f78e5,fdf5ec91b4c47347
X-Google-Attributes: gidf78e5,public
X-Google-ArrivalTime: 2003-01-28 10:51:47 PST
Path: archiver1.google.com!news1.google.com!newsfeed.stanford.edu!nntp.cs.ubc.ca!news-spur1.maxwell.syr.edu!news.maxwell.syr.edu!kibo.news.demon.net!mutlu.news.demon.net!demon!mail2news.demon.co.uk!devnull
From: o.v@k.ro (O.V.)
Newsgroups: comp.std.c++
Subject: Some features to be added to class-based OO languages
Date: Tue, 28 Jan 2003 18:51:44 +0000 (UTC)
Organization: http://groups.google.com/
Lines: 735
Approved: fjh@cs.mu.oz.au (Fergus Henderson , moderator of comp.std.c++)
Message-ID: <59143289.0301280527.1e95174d@posting.google.com>
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: mail2news.demon.co.uk 1043779904 10879 10.0.0.1 (28 Jan 2003 18:51:44 GMT)
X-Complaints-To: abuse@demon.net
NNTP-Posting-Date: Tue, 28 Jan 2003 18:51:44 +0000 (UTC)
X-Received: from mulga.cs.mu.oz.au ([128.250.1.22])
	by news.demon.co.uk with esmtp (Exim 4.05)
	id 18daph-0002pK-00
	for mail2news@news.news.demon.net; Tue, 28 Jan 2003 18:51:42 +0000
X-Received: from localhost (localhost [[UNIX: localhost]]) by mulga.cs.mu.OZ.AU
	id FAA07038; Wed, 29 Jan 2003 05:51:36 +1100 (EST)
X-Authentication-Warning: mulga.cs.mu.OZ.AU: fjh set sender to devnull@stump.algebra.com using -f
X-Path: comp-std-cpp-robomod!not-for-mail
X-Robomod: STUMP, ichudov@algebra.com (Igor Chudov)
X-Delivered-To: std-c++@ncar.ucar.edu
X-Newsgroups: comp.std.c++
X-NNTP-Posting-Date: 28 Jan 2003 13:27:33 GMT
X-MailScanner: PASSED (v1.2.7 4649 h0SDRYuF011048 mailbox3.ucsd.edu)
X-Spam-Status: No, hits=-0.2 required=5.0
	tests=BALANCE_FOR_LONG_20K,ITS_LEGAL,NOSPAM_INC,SPAM_PHRASE_00_01
	version=2.41
Xref: archiver1.google.com comp.std.c++:17584


===================================== 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                       ]



