www.digitalmars.com         C & C++   DMDScript  

D - How to avoid code duplication in D? MI/CI/delegation/C&P?

reply Yu Qian Zhou <yqz comlab.ox.ac.uk> writes:
Admittedly Java's single inheritance is easy to implement, and simple to
use.  But it does have some defect which sometimes force a programmer to
duplicate code by 'copy & paste':

Suppose I have two classes 'Car' and 'Dog', which *have already inherited*
from some more important classes 'Vehicle' and 'Animal' respectively
(let's assume that 'Vehicle' and 'Dog' do not have a common modifiable
ancestor); now I want both these two classes to implement another
interface 'Idable', how can I do it?

Answer:
=09  Java: Copy & Paste,               code duplication !!!
=09   C++: Multiple inheritance,    no code duplication
=09   C++: Textual code inclusion,  no code duplication
=09Eiffel: Multiple inheritance,    no code duplication
=09Sather: Semantic code inclusion, no code duplication
          Kiev: MI by delegation,        no code duplication

so what's the solution D can provide?

=09     D: ???                         code duplication ???

D borrowed DbC from Eiffel, perhaps it also worth to check the multiple
inheritance mechanism of Eiffel, which is much cleaner than C++ (by
renaming and redefinition).

Or if you still feel strongly against MI, maybe you can also consider
Sather's semantic code inclusion, or Kiev's forward-able delegation?

Let me know your thoughts on this.

YuQian


Sample code:
/***********************************************************************
  Java: Copy & Paste, code duplication
***********************************************************************/
interface Idable
{
  public   void setId(int i);
  public   void getId();
}

class      Car
extends    Vehicle
implements Idable
{
  private  int id;
  public   void setId(int i) { id =3D i;   }      =20
  public   void getId()      { return i; }
}


class      Dog=20
extends    Animal
implements Idable
{
  private  int id;=09=09=09=09// copy
  public   void setId(int i) { id =3D i;   }    =09// &
  public   void getId()      { return i; }    =09// paste!
}

/***********************************************************************
  Kiev:  Multiple inheritance by delegation, no code duplication
***********************************************************************/
class Id
{
private int id;
public  void setId(int i) { id =3D i;   }      =20
public  void getId()      { return i; }
}

class  =A0Car=A0
extends Vehicle
{
=A0=A0=A0=A0forward=A0public=A0Id myId;   // forward car.getId() to car.myI=
d.getId()
}

class  =A0Dog
extends Animal
{
=A0=A0=A0=A0forward=A0public=A0Id myId;   // forward dog.getId() to dog.myI=
d.getId()
}

/***********************************************************************
  C++:  Multiple inheritance, no code duplication
***********************************************************************/
class Idable
{
private: int id;
public:
   void setId(int i) { id =3D i;   }      =20
   void getId()      { return i; }
};

class Car: public Vehicle, Idable {};
class Dog: public Animal,  Idable {};

/***********************************************************************
  C++:  Textual code inclusion, no code duplication
***********************************************************************/

//--- begin file: id.ci ---
private: int id;
public:
   void setId(int i) { id =3D i;   }      =20
   void getId()      { return i; }
//--- end   file: id.ci ---


class Car: public Vehicle
{
#include "id.ci"
};

class Dog: public Animal
{
#include "id.ci"
};
Aug 29 2001
next sibling parent reply "Walter" <walter digitalmars.com> writes:
The way to do it is to use interfaces:

    class Car : Vehicle, Idable
    {
    }

Base classes after the first base class are interface classes.

-Walter

-----------------------------------------------------------------

Yu Qian Zhou wrote in message ...

Admittedly Java's single inheritance is easy to implement, and simple to
use.  But it does have some defect which sometimes force a programmer to
duplicate code by 'copy & paste':

Suppose I have two classes 'Car' and 'Dog', which *have already inherited*
from some more important classes 'Vehicle' and 'Animal' respectively
(let's assume that 'Vehicle' and 'Dog' do not have a common modifiable
ancestor); now I want both these two classes to implement another
interface 'Idable', how can I do it?

Answer:
  Java: Copy & Paste,               code duplication !!!
   C++: Multiple inheritance,    no code duplication
   C++: Textual code inclusion,  no code duplication
Eiffel: Multiple inheritance,    no code duplication
Sather: Semantic code inclusion, no code duplication
          Kiev: MI by delegation,        no code duplication

so what's the solution D can provide?

     D: ???                         code duplication ???

D borrowed DbC from Eiffel, perhaps it also worth to check the multiple
inheritance mechanism of Eiffel, which is much cleaner than C++ (by
renaming and redefinition).

Or if you still feel strongly against MI, maybe you can also consider
Sather's semantic code inclusion, or Kiev's forward-able delegation?

Let me know your thoughts on this.

YuQian


Sample code:
/***********************************************************************
  Java: Copy & Paste, code duplication
***********************************************************************/
interface Idable
{
  public   void setId(int i);
  public   void getId();
}

class      Car
extends    Vehicle
implements Idable
{
  private  int id;
  public   void setId(int i) { id = i;   }
  public   void getId()      { return i; }
}


class      Dog
extends    Animal
implements Idable
{
  private  int id; // copy
  public   void setId(int i) { id = i;   }    // &
  public   void getId()      { return i; }    // paste!
}

/***********************************************************************
  Kiev:  Multiple inheritance by delegation, no code duplication
***********************************************************************/
class Id
{
private int id;
public  void setId(int i) { id = i;   }
public  void getId()      { return i; }
}

class  Car
extends Vehicle
{
forward public Id myId;   // forward car.getId() to car.myId.getId()
}

class  Dog
extends Animal
{
forward public Id myId;   // forward dog.getId() to dog.myId.getId()
}

/***********************************************************************
  C++:  Multiple inheritance, no code duplication
***********************************************************************/
class Idable
{
private: int id;
public:
   void setId(int i) { id = i;   }
   void getId()      { return i; }
};

class Car: public Vehicle, Idable {};
class Dog: public Animal,  Idable {};

/***********************************************************************
  C++:  Textual code inclusion, no code duplication
***********************************************************************/

//--- begin file: id.ci ---
private: int id;
public:
   void setId(int i) { id = i;   }
   void getId()      { return i; }
//--- end   file: id.ci ---


class Car: public Vehicle
{
#include "id.ci"
};

class Dog: public Animal
{
#include "id.ci"
};
Aug 29 2001
next sibling parent reply Dan Hursh <hursh infonet.isl.net> writes:
Walter wrote:
 
 The way to do it is to use interfaces:
 
     class Car : Vehicle, Idable
     {
     }
 
 Base classes after the first base class are interface classes.
 
 -Walter

Now I'm confused. Can a class inherit implementation code from an interface? If so then I've lost the difference between an interface and multiple inheritance. This also doesn't handle the case where Vehicle and Idable are existing classes (from a library?) that you would rather reuse instead of re-writing. Dan
Aug 29 2001
parent reply "Walter" <walter digitalmars.com> writes:
Dan Hursh wrote in message <3B8DD17F.CAD2F377 infonet.isl.net>...
Walter wrote:
 The way to do it is to use interfaces:
     class Car : Vehicle, Idable
     {
     }
 Base classes after the first base class are interface classes.

interface?

No. An interface is just a function declaration, no code.
If so then I've lost the difference between an interface and
multiple inheritance.  This also doesn't handle the case where Vehicle
and Idable are existing classes (from a library?) that you would rather
reuse instead of re-writing.

If they are existing classes, then include them as members rather than as bases.
Sep 01 2001
parent reply Yu Qian Zhou <yqz comlab.ox.ac.uk> writes:
 Walter wrote:

 If so then I've lost the difference between an interface and
 multiple inheritance.  This also doesn't handle the case where
 Vehicle and Idable are existing classes (from a library?) that you
 would rather reuse instead of re-writing.

If they are existing classes, then include them as members rather than as bases.

But if the existing class has *many* methods you want to use, the programmer will need to wrapper all of them: class NewClass { ExistingClass dummy; // declare the member void foo(arg, ...) { return dummy.foo( arg ... ); } // delegate // repeat for *all* the methods of ExistingClass ... ... } This approach is very tedious and error-prone. That's the reason why Kiev introduce the 'forward' keyword: class NewClass { forward ExistingClass dummy; // newClass.foo(...) is automatically // forwarded to newClass.dummy.foo(...) // by the *compiler* } This solution is not perfect, but as long as there is no conflict it at least saves the programmers' labour, and it helps to reduce some possible programming errors. BTW, Walter, have you decided to introduce any better approach into D other than Java's single inheritance + interface + "Copy&Paste" ? YuQian
Sep 01 2001
parent reply "Walter" <walter digitalmars.com> writes:
Yu Qian Zhou wrote in message ...
BTW, Walter, have you decided to introduce any better approach into D
other than Java's single inheritance + interface + "Copy&Paste" ?

No, I haven't. I understand that not having MI will result in some code duplication. I don't think, however, it is a cost that exceeds the greater understandability of non-MI code.
Sep 02 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
 Yu Qian Zhou wrote in message ...
BTW, Walter, have you decided to introduce any better approach into D
other than Java's single inheritance + interface + "Copy&Paste" ?

No, I haven't. I understand that not having MI will result in some code duplication. I don't think, however, it is a cost that exceeds the greater understandability of non-MI code.

I have to agree with Yu Qian, I've programmed some GUI's and java and sometimes you've objects which have implement several interfaces, but often only a single function of each. A good example are the 'Listeners' java has. A listener would be an event slot in qt language. You get all the GUI actions into there. Example were something like KeyboardListener, MouseListener, etc. Suprisingly the java API always provides each listener twice, once as an interface, and once as a normal class you can inherit. If the situation allows it you can inherit the Listener class and just overload the functions you want, but in some situations you alread inherit from something else. Then you've to implement the interface and write code for -every- function it provides. This is the code duplication how I think Yu Qian tried to express. In my eyes somebody thought simple single inheritence was enough. Sounds like a good idea. But in pratice they soon discovered that there are cases it does not suffice, like in many cases one often seen example in java is 'Application' and 'Runable'. Okay you've to inherit for an Application to be start able. If you want to run another thread you've to implement the 'Runable' interface, because you can't overload also the 'Thread' class since you are already an application. In this dilemma the interfaces were introduced, a suitable workaround. But exactly in this example writing threaded code by inheriting 'Thread' is far more convient than implementing the 'Runable' interface which requires you to write more code, and to create an additional thread object as 'branch'. I think single inheritence was an expiriment, worth taking altough. But looking at the results my impression is it failed. Why can't I just inherit both from 'Application' and from 'Thread' at the same time? I don't think MI is any bad if you implement it stronger typed than C++ did. A function is eitherway absolute unique identifyable or a compile error will be raised. The worst case that can happen is that the user has to specify from which parent he wants to call the function Foo() if both should have them. Another aspect which make C++ inheritance complicated is public, protected and private inheritance. Something I never understood what the real benefits of it are. In the sense IS-A and HAS-A, I can hide what I have, but I should not be able to hide what I am. Diamond inheritance? If it makes problems, just leave it away. You're still far more powerfull than single inheritence would provide. - Axel
Sep 02 2001
next sibling parent reply "kaffiene" <kaffiene xtra.co.nz> writes:
"Axel Kittenberger" <axel dtone.org> wrote in message
news:9mst9n$2o41$1 digitaldaemon.com...
 Yu Qian Zhou wrote in message ...
BTW, Walter, have you decided to introduce any better approach into D
other than Java's single inheritance + interface + "Copy&Paste" ?

No, I haven't. I understand that not having MI will result in some code duplication. I don't think, however, it is a cost that exceeds the


 understandability of non-MI code.

I have to agree with Yu Qian, I've programmed some GUI's and java and sometimes you've objects which have implement several interfaces, but

 only a single function of each. A good example are the 'Listeners' java
 has. A listener would be an event slot in qt language. You get all the GUI
 actions into there. Example were something like KeyboardListener,
 MouseListener, etc. Suprisingly the java API always provides each listener
 twice, once as an interface, and once as a normal class you can inherit.

 the situation allows it you can inherit the Listener class and just
 overload the functions you want, but in some situations you alread inherit
 from something else. Then you've to implement the interface and write code
 for -every- function it provides. This is the code duplication how I think
 Yu Qian tried to express.

Java does not always provide classes that instantiate interfaces, it's usually only done for interfaces that have a large number of methods and for which it is valid to leave all the methods empty (so you only specifiy the ones you want and leave everything else as empty). I think Walter's point about the readibility trade off is a good one - the cost you mention is slight (and providing a do-nothing implementation of large interfaces is a one-off cost paid by writers of library code - big deal! :-) )
 In my eyes somebody thought simple single inheritence was enough. Sounds
 like a good idea. But in pratice they soon discovered that there are cases
 it does not suffice, like in many cases one often seen example in java is
 'Application' and 'Runable'. Okay you've to inherit for an Application to
 be start able. If you want to run another thread you've to implement the
 'Runable' interface, because you can't overload also the 'Thread' class
 since you are already an application. In this dilemma the interfaces were
 introduced, a suitable workaround. But exactly in this example writing
 threaded code by inheriting 'Thread' is far more convient than

 the 'Runable' interface which requires you to write more code, and to
 create an additional thread object as 'branch'.

Interfaces were not introduced as a workaround to not having MI in Java, they were implemented from the start as a concious design choice.
 I think single inheritence was an expiriment, worth taking altough. But
 looking at the results my impression is it failed.

Well, there are a lot of people who would say that Java provides proof that the need for MI is slim. One of the things that I hear most from people who like Java is that it produces much cleaner, more maintainable code that C++.
Why can't I just inherit
 both from 'Application' and from 'Thread' at the same time? I don't think
 MI is any bad if you implement it stronger typed than C++ did. A function
 is eitherway absolute unique identifyable or a compile error will be
 raised. The worst case that can happen is that the user has to specify

 which parent he wants to call the function Foo() if both should have them.

Eiffel has a resolution system that allows the programmer to disambiguate parent symbols in this way. From all accounts, it seems to work quite well.
 Another aspect which make C++ inheritance complicated is public, protected
 and private inheritance. Something I never understood what the real
 benefits of it are. In the sense IS-A and HAS-A, I can hide what I have,
 but I should not be able to hide what I am.

 Diamond inheritance? If it makes problems, just leave it away. You're

 far more powerfull than single inheritence would provide.

 - Axel

Sep 02 2001
parent Axel Kittenberger <axel dtone.org> writes:
 Java does not always provide classes that instantiate interfaces, it's
 usually only done for interfaces that have a large number of methods and
 for which it is valid to leave all the methods empty (so you only specifiy
 the ones you want and leave everything else as empty).  

At least for almost all the 'Listener' interfaces there are empty classes. The parallels are not often seeable at first sight like the other example I brought already the 'Thread' class vs. the 'Runable' as signle inheritance workaround interface.
 Interfaces were not introduced as a workaround to not having MI in Java,
 they were implemented from the start as a concious design choice.

What you've seen as release 1.0 of java has also an evolution behind it.
 Well, there are a lot of people who would say that Java provides proof
 that the need for MI is slim.  One of the things that I hear most from 
 people who like Java is that it produces much cleaner, more maintainable 
 code that C++.

That java produces far mor maintainable code than C++ is no secret, but this is an effeact that results from the summeration of all differences. MI must not be necessarly one of these. I think the miss of general pointers, the strong exceptions, the garbadge collector memory managment and all that proive easier maintainable code, one can't cleanly say this is because of having SI over MI. - Axel
Sep 02 2001
prev sibling next sibling parent reply Dan Hursh <hursh infonet.isl.net> writes:
Axel Kittenberger wrote:
 Another aspect which make C++ inheritance complicated is public, protected
 and private inheritance. Something I never understood what the real
 benefits of it are. In the sense IS-A and HAS-A, I can hide what I have,
 but I should not be able to hide what I am.

I think the is one of the interesting this C++ got convoluted. I like the way LX handled this. Think of it this way, in C you have: Interface: you get the ISA relationship but no data or implementation (Yes, this isn't C++ but you get it with a virtual base class. Interfaces aren't bad, just not the answer to everything.) Public Inheritance: You get ISA, implementation and data Private Inheritance: You don't get ISA, but you get implementation and data Only your closest 'friend' know where you got it Protected Inheritance: You don't get ISA, but you get implementation and data Only your children know the family secret. In LX there are to basic form of inheritance, you have inherit the interface from an object giving you the ISA relationship. You can then separately inherit the implementation. You are still free to override if I recall correctly. This doesn't support the cases were you only have the ISA relation for a small subset of code. I'm am curious if and how that would be missed. In the end I think LX makes it clearer by separating the two forms of inheritance and let you do one the other or both for a class. It requires two clauses to get the equivalent of C++'s public inheritance, but I still think it makes it easier to understand what you are getting.
 Diamond inheritance? If it makes problems, just leave it away. You're still
 far more powerfull than single inheritence would provide.

In D you will always have Diamond inheritance because of the Object base class. Living without Diamond inheritance in an MI world is probably harder than living without MI in an SI world. If you have MI, Diamond just seem to happen and the Object base is a great example why. I imagine frameworks have that problem a lot too. Disabling diamonds in MI would probably turn into an example of how idealism crippled a feature. This simple fact is that Walter is right. C++ buggered up MI, and I think the only way to get it into D is if we can find a set of semantics that - has the useful MI feature that you can't get with SI and interfaces - that doesn't have the ambiguities of C++ style inheritance - won't turn the compiler into an AI experiment - that does not require Ph.D.. level knowledge to fully grasp. (I challenge anyone to explain C++ inheritance fully and clearly in one page, 8 pt. font, 1 inch margins.) I've heard people take about other languages getting it better, but I am not familiar enough with any of the languages to argue there merits or flaws. Any ideas? Remember that operator overloading and generic programming are still on the table, so be careful not to rule those out. Dan
Sep 02 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
 I think the is one of the interesting this C++ got convoluted.  I like
 the way LX handled this.  Think of it this way, in C you have:

Well to pendantic in C (without ++) you've no objects at all :o)
 Interfaces aren't bad, just not the answer to everything.)

Nothing is the answer to everything, and I dislike languages that present themselfs as the ultimate answer.
 Private Inheritance: You don't get ISA, but you get implementation and
 data
 Only your closest 'friend' know where you got it

Hmmm, but what is here now the real difference to HASA?
 In LX there are to basic form of inheritance, you have inherit the
 interface from an object giving you the ISA relationship.  You can then
 separately inherit the implementation.  You are still free to override
 if I recall correctly.  This doesn't support the cases were you only
 have the ISA relation for a small subset of code.  I'm am curious if and
 how that would be missed.

That sounds to be produce quite 'expensive' runtime code (assembler). I would stick with the user having to write clear interface or the same in MI abstract classes. Overrideable functions requires 'invisible' function pointers. (the virtual keyword in C++)
 In D you will always have Diamond inheritance because of the Object
 base class.  

Well the 'Object' class has on most systems a special status, (most trivial is the implicit inheritance,)
 Living without Diamond inheritance in an MI world is
 probably harder than living without MI in an SI world.  

Thats pretty confusing. There are no sperated MI and SI worlds, MI without diamonds is still plain and simply more powerfull than SI. Remember in MI you can still create interface through pure abstract classes.
 If you have MI,
 Diamond just seem to happen and the Object base is a great example why.
 I imagine frameworks have that problem a lot too.  Disabling diamonds in
 MI would probably turn into an example of how idealism crippled a
 feature.

That very same crippled statement you can say about SI vs. MI.
 This simple fact is that Walter is right.  C++ buggered up MI, and I
 think the only way to get it into D is if we can find a set of semantics
 that
 
 - has the useful MI feature that you can't get with SI and interfaces
 - that doesn't have the ambiguities of C++ style inheritance
 - won't turn the compiler into an AI experiment
 - that does not require Ph.D.. level knowledge to fully grasp.
 (I challenge anyone to explain C++ inheritance fully and clearly
 in one page, 8 pt. font, 1 inch margins.)

That goes to all language features :o)
 I've heard people take about other languages getting it better, but I am
 not familiar enough with any of the languages to argue there merits or
 flaws.  Any ideas?  Remember that operator overloading and generic
 programming are still on the table, so be careful not to rule those out.

Well I'm not trying to move D in a special direction, as many seem here to do :/ So far I found this newsgroup an invaluable resource to get the 'Zeitgeist' people want today from a language. I've also efforts going writing a new language, however in my case it's a pure hobby. - Axel -- [D) http://www.dtone.org
Sep 02 2001
parent reply Dan Hursh <hursh infonet.isl.net> writes:
Axel Kittenberger wrote:
 
 I think the is one of the interesting this C++ got convoluted.  I like
 the way LX handled this.  Think of it this way, in C you have:

Well to pendantic in C (without ++) you've no objects at all :o)

Well, in my little world things are different .... :-)
 Private Inheritance: You don't get ISA, but you get implementation and
 data
 Only your closest 'friend' know where you got it

Hmmm, but what is here now the real difference to HASA?

It says the derived class has all the same HASA relationships as the parent, with out manually cutting and pasting code and forwarding method calls. On the other hand, a pointer to the parent class cannot be assigned a reference to the child class. It is for code reuse, but not polymorphism.
 In LX there are to basic form of inheritance, you have inherit the
 interface from an object giving you the ISA relationship.  You can then
 separately inherit the implementation.  You are still free to override
 if I recall correctly.  This doesn't support the cases were you only
 have the ISA relation for a small subset of code.  I'm am curious if and
 how that would be missed.

That sounds to be produce quite 'expensive' runtime code (assembler). I would stick with the user having to write clear interface or the same in MI abstract classes. Overrideable functions requires 'invisible' function pointers. (the virtual keyword in C++)

Well, I can't say for sure myself. I don't think it would be expensive though. The implementation inheritance is just a matter the the compiler doing a cut and paste on the programmer's behalf. The interface inheritance would be the same as inheriting from a pure virtual base class, even if it isn't pure virtual. LX was pretty flexible too about these things. It looked like LX would have performance problems because of some of it's features, but this isn't one I expected to cause problems.
 In D you will always have Diamond inheritance because of the Object
 base class.

Well the 'Object' class has on most systems a special status, (most trivial is the implicit inheritance,)
 Living without Diamond inheritance in an MI world is
 probably harder than living without MI in an SI world.

Thats pretty confusing. There are no sperated MI and SI worlds, MI without diamonds is still plain and simply more powerfull than SI. Remember in MI you can still create interface through pure abstract classes.

It wasn't a very clear statement, but basically, the diamond shaped inheritance graph is pretty common in when using MI to build frameworks. This obviously isn't a problem in SI, because you don't have the necessary features to get you to that inheritance problem. On the other hand, divide by zero wouldn't be a problem either if we would just get rid of that nasty division operator.
 If you have MI,
 Diamond just seem to happen and the Object base is a great example why.
 I imagine frameworks have that problem a lot too.  Disabling diamonds in
 MI would probably turn into an example of how idealism crippled a
 feature.

That very same crippled statement you can say about SI vs. MI.

You won't get any argument for me here. It's just frameworks often like to have common base classes and combining two type derived from the common root would be pretty common in C++ I would think. I'd just hate to add a crippled MI implementation and have someone say "There! Now stop complaining!" I also don't want another bad implementation of MI that would give opponents of MI more ammunition to claim the entire paradigm is flawed.
 This simple fact is that Walter is right.  C++ buggered up MI, and I
 think the only way to get it into D is if we can find a set of semantics
 that

 - has the useful MI feature that you can't get with SI and interfaces
 - that doesn't have the ambiguities of C++ style inheritance
 - won't turn the compiler into an AI experiment
 - that does not require Ph.D.. level knowledge to fully grasp.
 (I challenge anyone to explain C++ inheritance fully and clearly
 in one page, 8 pt. font, 1 inch margins.)

That goes to all language features :o)

Now hey, C++ didn't bugger everything. Seriously though, C++ got so complex because of how it tried to resolve ambiguities. This is true of it's MI and templates. Java inheritance would be pretty easy to explain by comparison. They simply recognized the C++ MI implementation had problems, so they lopped off a leg to fix a problem in the foot. Now let me get back to those divide by zero problems. You know a naval ship was dead in the water as a result of a divide by zero. The division operator is just too dangerous. Besides, we can bit shift. It's the same as dividing by powers of two without all the dangers.
 I've heard people take about other languages getting it better, but I am
 not familiar enough with any of the languages to argue there merits or
 flaws.  Any ideas?  Remember that operator overloading and generic
 programming are still on the table, so be careful not to rule those out.

Well I'm not trying to move D in a special direction, as many seem here to do :/ So far I found this newsgroup an invaluable resource to get the 'Zeitgeist' people want today from a language. I've also efforts going writing a new language, however in my case it's a pure hobby.

I want someone to take this in a special direction. One as functional as C++ without the convoluted rules. If other languages did it better, I'd like to know how. I'm floundering here. Dan
Sep 02 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
 Private Inheritance: You don't get ISA, but you get implementation and
 data
 Only your closest 'friend' know where you got it

Hmmm, but what is here now the real difference to HASA?

It says the derived class has all the same HASA relationships as the parent, with out manually cutting and pasting code and forwarding method calls. On the other hand, a pointer to the parent class cannot be assigned a reference to the child class. It is for code reuse, but not polymorphism.

If it's private which calls should be forwarded? Is there any good example where private inheritance really makes sense? Against having a normal private field, HASA.
 Well, I can't say for sure myself.  I don't think it would be expensive
 though.  The implementation inheritance is just a matter the the
 compiler doing a cut and paste on the programmer's behalf.  The
 interface inheritance would be the same as inheriting from a pure
 virtual base class, even if it isn't pure virtual.  LX was pretty
 flexible too about these things.  It looked like LX would have
 performance problems because of some of it's features, but this isn't
 one I expected to cause problems.

Well I don't want to be harsh to cristopher, as the same goes for my own project. But how much code is actually coded in LX? From my own expirience I can tell a lot of things look good on paper first, but when you try to work with it, you discover it's 'cribbled' or has logical conflicts. We'll just have to look how ideas proof themselfs in practice. It's the old science paradigm that in example the greeks did wrong. They theoritisied the whole time only, without paying attention to the expiriment. I think in basic the same ideology should be maintained also in programming environment, expirimients tell what's true or false, not theories.
 They simply recognized the C++ MI implementation had
 problems, so they lopped off a leg to fix a problem in the foot.
 Now let me get back to those divide by zero problems.  You know a naval
 ship was dead in the water as a result of a divide by zero.  The
 division operator is just too dangerous.  Besides, we can bit shift.
 It's the same as dividing by powers of two without all the dangers.

Yup that's a good argument MI has it's dangers, the division operator has it's dangers. But both have they're advantages, now you can scrap both and workaround them, or you can live with the danger. Division by zero is a result of another bug somewhere, not a problem itself. I've a nicer example, the Ariane rocket. Had an integer overflow, and suddendly changed it's mind from: "I want to go up, I want to go up, I want to go up" to "I want to turn around and thrust down", until the computer decided to explode the rocket. - Axel
Sep 02 2001
next sibling parent "Walter" <walter digitalmars.com> writes:
Axel Kittenberger wrote in message <9mv2te$s4h$1 digitaldaemon.com>...
We'll just have to look how ideas proof themselfs in practice.
It's the old science paradigm that in example the greeks did wrong. They
theoritisied the whole time only, without paying attention to the
expiriment. I think in basic the same ideology should be maintained also in
programming environment, expirimients tell what's true or false, not
theories.

That is so true. I've had to redo D a couple times because I'd write a program, and find that what looked great in my head didn't work in a real program.
Sep 02 2001
prev sibling next sibling parent reply Dan Hursh <hursh infonet.isl.net> writes:
Axel Kittenberger wrote:
 
 Private Inheritance: You don't get ISA, but you get implementation and
 data
 Only your closest 'friend' know where you got it

Hmmm, but what is here now the real difference to HASA?

It says the derived class has all the same HASA relationships as the parent, with out manually cutting and pasting code and forwarding method calls. On the other hand, a pointer to the parent class cannot be assigned a reference to the child class. It is for code reuse, but not polymorphism.

If it's private which calls should be forwarded? Is there any good example where private inheritance really makes sense? Against having a normal private field, HASA.

Nothing is coming to mind. I'm pretty sure I've had good uses for private inheritance in previous projects, but not many. It would be a lot more useful if the method retained their access classification and you just did get the ISA relationship from the base class.
 Well, I can't say for sure myself.  I don't think it would be expensive
 though.  The implementation inheritance is just a matter the the
 compiler doing a cut and paste on the programmer's behalf.  The
 interface inheritance would be the same as inheriting from a pure
 virtual base class, even if it isn't pure virtual.  LX was pretty
 flexible too about these things.  It looked like LX would have
 performance problems because of some of it's features, but this isn't
 one I expected to cause problems.

Well I don't want to be harsh to cristopher, as the same goes for my own project. But how much code is actually coded in LX? From my own expirience I can tell a lot of things look good on paper first, but when you try to work with it, you discover it's 'cribbled' or has logical conflicts.

True, it's a new idea, just worked better in my head than the styles of inheritance in C++. For that matter I can't remember if LX had access control on members.
 We'll just have to look how ideas proof themselfs in practice.
 It's the old science paradigm that in example the greeks did wrong. They
 theoritisied the whole time only, without paying attention to the
 expiriment. I think in basic the same ideology should be maintained also in
 programming environment, expirimients tell what's true or false, not
 theories.

True, but it is good that there are still folks that are theorizing. :-)
 They simply recognized the C++ MI implementation had
 problems, so they lopped off a leg to fix a problem in the foot.
 Now let me get back to those divide by zero problems.  You know a naval
 ship was dead in the water as a result of a divide by zero.  The
 division operator is just too dangerous.  Besides, we can bit shift.
 It's the same as dividing by powers of two without all the dangers.

Yup that's a good argument MI has it's dangers, the division operator has it's dangers. But both have they're advantages, now you can scrap both and workaround them, or you can live with the danger. Division by zero is a result of another bug somewhere, not a problem itself. I've a nicer example, the Ariane rocket. Had an integer overflow, and suddendly changed it's mind from: "I want to go up, I want to go up, I want to go up" to "I want to turn around and thrust down", until the computer decided to explode the rocket.

Geez! Maybe we should just use variable length text to represent numbers. Of course that still doesn't fix divide by zero. I still say we drop it. Dan
Sep 02 2001
parent reply "Sean L. Palmer" <spalmer iname.com> writes:
I've always thought that if you divide by zero you should get infinity (if
the dividend was positive) or negative infinity (if the dividend was
negative).  With modulo by zero the answer is always zero.  I'm not so sure
I agree with my old line of thinking these days though.  It's probably
better to organize the code against preventing division by zero than by
responding to it after the fact (may save a division, but requires some
comparisons first, as well as some knowledge about the limitations of your
floating point representation).

I just don't know, I'm so used to DBZ by now it doesn't bother me anymore.

Sean


 Geez!  Maybe we should just use variable length text to represent
 numbers.  Of course that still doesn't fix divide by zero.  I still say
 we drop it.

 Dan

Oct 24 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
Sean L. Palmer wrote:

 I've always thought that if you divide by zero you should get infinity (if
 the dividend was positive) or negative infinity (if the dividend was
 negative).  With modulo by zero the answer is always zero.  I'm not so
 sure
 I agree with my old line of thinking these days though.  It's probably
 better to organize the code against preventing division by zero than by
 responding to it after the fact (may save a division, but requires some
 comparisons first, as well as some knowledge about the limitations of your
 floating point representation).

Mathematically a division by zero does not give infinity, but is undefined. A devision of _nearly_ zero gives _nearly_ infinity. Take following sentence out of: a / b = c --follows--> a = b * c I guess thats a pretty *dough* sentence :o) Take in example the proposal: 2 / 0 = infinity then it would have to be that 0 * inifity = ? 2 ? --- See? Infinity is not quite right. Maths can calculate with _nearly_ zero and _nearly_ infinity, like the a * sin (x) lim --------- x -> 0 x Would give mathematically a defined result, but hence we do programming here not hardcore maths :o)
 Geez!  Maybe we should just use variable length text to represent
 numbers.  Of course that still doesn't fix divide by zero.  I still say
 we drop it.

Division by zero is an error, and should raise an exception, a hardware trap,etc.
Oct 24 2001
parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Axel Kittenberger wrote:

 Maths can calculate with _nearly_ zero and _nearly_ infinity, like the
                 a * sin (x)
   lim         ---------
  x -> 0             x

Also remember that you can have "positive zero" and "negative zero" when dealing with limits: abs(x) lim ------- = 1 x -> 0+ x abs(x) lim ------- = -1 x -> 0- x -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Oct 24 2001
prev sibling parent reply Iliya Peregoudov <iliyap mail.ru> writes:
Why don't use another method for late methods binding?

Multiple inheritance in C++ is so clumsy due to using of virtual
tables.  There is another method used in Objective-C -- dispatching
tables, also known as selector tables.

Objective-C is loosely typed language that support completely
anonymous object type (id).  But this is done just to make it more
similar to Smalltalk.  It is possible to make a strictly typed
language that use dispatching tables.  [Dispatching table for the class
contains entries only for those methods, that had been defined in this
class.  It also contains a link to superclass dispatch table.  If
required method is not found in the its class dispatch table it will be
searched in superclass dispatch table and so on.]  Objective-C doesn't
support multiple inheritance.  But its model can be easily extended to
support it -- just let the dispatch table contain multiple links to
superclasses.

Dispatch tables also convenient for various GUI frameworks that define
palymorphing methods for event handlers.  The common problem is that
there are too many events to be handled.  If you make method for each
handler the vtables grow extremely for each GUI framework class.
This is because most frameworks doesn't use virtual methods for
event handlers (for example, Borland VCL and OWL use dynamic methods
that use exactly the same dispatch tables, MFC uses tables that are
similar to dispatch tables).

So, why don't use dispatch tables instead of virtual tables?

-- iliyap
Sep 07 2001
next sibling parent reply "Walter" <walter digitalmars.com> writes:
Iliya Peregoudov wrote in message <3B98B78E.7232 mail.ru>...
So, why don't use dispatch tables instead of virtual tables?

Virtual tables have the advantage of fast execution speed.
Sep 08 2001
parent reply Iliya Peregoudov <iliyap mail.ru> writes:
Walter wrote:
 
 Iliya Peregoudov wrote in message <3B98B78E.7232 mail.ru>...
So, why don't use dispatch tables instead of virtual tables?

Virtual tables have the advantage of fast execution speed.

They're not so inefficient as it seems. The Objective-C run-time system, for example, maintains a method cache for ecach class. When the method is called for the object it is cached in the object's class method cache. When the method called again it looked up in the cache. Method cache is a flat table so cache lookups are just a little slower than vtable lookups.
Sep 09 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
Iliya Peregoudov wrote:

 Walter wrote:
 
 Iliya Peregoudov wrote in message <3B98B78E.7232 mail.ru>...
So, why don't use dispatch tables instead of virtual tables?

Virtual tables have the advantage of fast execution speed.

They're not so inefficient as it seems. The Objective-C run-time system, for example, maintains a method cache for ecach class. When the method is called for the object it is cached in the object's class method cache. When the method called again it looked up in the cache. Method cache is a flat table so cache lookups are just a little slower than vtable lookups.

That's but why use them if they are just a little slower? What advantage do they bring if weak type binding is not supported at all, just a little slower for what? As far I understood the memory occupied by the one super-table is not less than the sum of all vtables for every object. Why do caching if the compiler can already write the precalculated value into the assembler code as vtable? A vtable call constist of vtable[MAGIC_KEY](arguments), so it's composed of vtable + MAGIC_KEY, get content of, call. That's are 3 assembler level commands. All I read about dispatch tables was only pure hiping on how they are cool, and how vtables are bad, but without no facts in the background. Of how much assembler compositions does a dispatch call take less than a vtable call? Or maybe a little example of two or three classes, where a dispatch table will take less memory than the 3 vtables. - Axel
Sep 09 2001
parent reply Iliya Peregoudov <iliyap mail.ru> writes:
Axel Kittenberger wrote:
 
 Iliya Peregoudov wrote:
 
 Walter wrote:
 Iliya Peregoudov wrote in message <3B98B78E.7232 mail.ru>...
So, why don't use dispatch tables instead of virtual tables?

Virtual tables have the advantage of fast execution speed.

They're not so inefficient as it seems. The Objective-C run-time system, for example, maintains a method cache for ecach class. When the method is called for the object it is cached in the object's class method cache. When the method called again it looked up in the cache. Method cache is a flat table so cache lookups are just a little slower than vtable lookups.

That's but why use them if they are just a little slower? What advantage do they bring if weak type binding is not supported at all, just a little slower for what? As far I understood the memory occupied by the one super-table is not less than the sum of all vtables for every object. Why do caching if the compiler can already write the precalculated value into the assembler code as vtable? A vtable call constist of vtable[MAGIC_KEY](arguments), so it's composed of vtable + MAGIC_KEY, get content of, call. That's are 3 assembler level commands. All I read about dispatch tables was only pure hiping on how they are cool, and how vtables are bad, but without no facts in the background. Of how much assembler compositions does a dispatch call take less than a vtable call? Or maybe a little example of two or three classes, where a dispatch table will take less memory than the 3 vtables. - Axel

The MAGIC_KEY mentioned is method index in vtable counted from (for example) zero. When a class inherits from two classes (multiple inheritance) method indexes overlap each other. So you'll need two vtables. If the class inherits from classes that already inherited from other classes (and so on) you'll end up with a great number of vtables for the class. This can be avoided if each method in the system takes its own unique MAGIC_KEY. For example, doSomething(int, char[]) is assigned 1001 (system can contain hungreds of classes with thousands of different methods). So if the class implemets method doSomething(int, char[]) it already has vtable 1002 items long (al least). Most slots of vtable will be unfilled. When using dispatch tables each method takes its own unique MAGIC_KEY named selector. But the dispatch table is not a linear array, but a hash, where selector is a key and method address is a value. Moreover this hash contains only those selector:method pairs that are defined in the class but not those that inherited. Cache contains only those selector:method pairs that are used at runtime for this class, not all selector:method pairs in the system. Hash lookups are slower then linear array lookups. But garbage collection is a also not so efficient than manual memory management. Why garbage collected programs are faster and less error prone than those using manual memory management?
Sep 09 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
 The MAGIC_KEY mentioned is method index in vtable counted from (for
 example) zero.  When a class inherits from two classes (multiple
 inheritance) method indexes overlap each other.  So you'll need two
 vtables.  If the class inherits from classes that already inherited
 from other classes (and so on) you'll end up with a great number of
 vtables for the class.

True, I didn't yet thought about vtables in combination with implementing differend interfaces at the same time. So the "problem" even exists with java-like SI inheritince not even only in multi-inheritance.
 This can be avoided if each method in the system takes its own unique
 MAGIC_KEY.  For example, doSomething(int, char[]) is assigned 1001
 (system can contain hungreds of classes with thousands of different
 methods). So if the class implemets method doSomething(int, char[])
 it already has vtable 1002 items long (al least).  Most slots of
 vtable will be unfilled.

"Most slots of vtable will be unfilled." That's false. In a vtable system not every function will have a global unique ID. Every function has a unique ID in the vtable for the class that started a polymorhpic function. If a class has 7 virtual functions it's vtable will be 7 entries long. If a class implements two interfaces each with 10 different virtual functions, it has 2 vtables with each 10 entries, so it are 20 entries in summeration. Having 1000 unfilled entries is not true, you can watch C++'s vtables in example in ddd/gdb in the debugger in action. The tables had always as much entries as I implemented virtual functions. Why can ID's be duplicated? Since the compiler know in the call to foo() that this function is in the foo interface, so it can take the id for the foo vtable.
 When using dispatch tables each method takes its own unique MAGIC_KEY
 named selector.  But the dispatch table is not a linear array,
 but a hash, where selector is a key and method address is a value.
 Moreover this hash contains only those selector:method pairs that
 are defined in the class but not those that inherited.  Cache contains
 only those selector:method pairs that are used at runtime for this
 class, not all selector:method pairs in the system.

Ahh, I understand know.... still I think system is pretty good for loosly bindend language. For strong typed ones, where all possiblites can be precalculated at compiletime I see neither a speed nor a memory gain. Take the class bork that implemnts the interfaces cork and dork. cork and dork have 10 functions. Then the tables use at the end: dork: 10 entries cork: 10 entries bork: 20 entries -- 50 entries in summeration. Now with a dispatch table imagined to be flat it would be 20 functions for 3 classes, so it are 60 entries. Okay now with hashing you can save the nulls. so the logic is not class but function based. So 10 functions from dork have to be counted 2 times (for dork and bork), and the 10 functions from cork are also counted two times (for cork and bork). So it are hmmm, 40 entries. Okay I understand :) Still I'm not convinced that the memory requirements pay that much of the hazzle for having to hash.
 But garbage collection is a also not so efficient than manual memory 
 management. Why garbage collected programs are faster and less error 
 prone than those using manual memory management?

Well this is a whole science itself. Garbadge collected programs -can- be faster then convetional ones. Since a lot of object coping can be saved sometimes.But this is not a must be. Especially gcc 3.0 vs 2.95 did not convince me. 3.0 uses now GC inside, but in summeration with all other edits it compiler far slower than 2.95. However to say it's due to the GC is false, since it's the sum of a lot of new stuff, and more sensetive warning checking etc. - Axel
Sep 09 2001
next sibling parent reply "Walter" <walter digitalmars.com> writes:
Well this is a whole science itself. Garbadge collected programs -can- be
faster then convetional ones. Since a lot of object coping can be saved
sometimes.But this is not a must be. Especially gcc 3.0 vs 2.95 did not
convince me. 3.0 uses now GC inside, but in summeration with all other
edits it compiler far slower than 2.95. However to say it's due to the GC
is false, since it's the sum of a lot of new stuff, and more sensetive
warning checking etc.

I'd be a little surprised if gc makes the compiler slower. Most compilations will not need more than available memory to compile, and if the gc is tuned to that, it will rarely need to actually run a collection.
Sep 09 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
Walter wrote:

Well this is a whole science itself. Garbadge collected programs -can- be
faster then convetional ones. Since a lot of object coping can be saved
sometimes.But this is not a must be. Especially gcc 3.0 vs 2.95 did not
convince me. 3.0 uses now GC inside, but in summeration with all other
edits it compiler far slower than 2.95. However to say it's due to the GC
is false, since it's the sum of a lot of new stuff, and more sensetive
warning checking etc.

I'd be a little surprised if gc makes the compiler slower. Most compilations will not need more than available memory to compile, and if the gc is tuned to that, it will rarely need to actually run a collection.

I know, but gcc made advertising GC difficut, since it was one of the most famous projects that really used garbadge collection, and with this release it turned out to compile at least double as long :/ I understand that it has nothing do to with each other, but it's a slap on "marketing". Try to call really a well-known project that runs fast with GC. Tell the people gcc 3.0 and you make a fool of yourself. Say java and you'll have to laugh yourself. I don't know of any others, and I understand the reasons behind speed penality are to be searched other where, but try to tell this :/ - Axel
Sep 09 2001
parent reply "Walter" <walter digitalmars.com> writes:
Axel Kittenberger wrote in message <9ngagf$1isq$1 digitaldaemon.com>...
Say java and you'll have to laugh
yourself. I don't know of any others, and I understand the reasons behind
speed penality are to be searched other where, but try to tell this :/

I've worked with Java and gc. GC can make a program faster, given: 1) write code that makes use of gc - for example, in C, people frequently copy strings to avoid memory ownership bugs. With gc, copy a reference, not the data. 2) close cooperation with the language. This, of course, doesn't work with C++. 3) much temporary generation in C++ can go away, again because of (1). 4) Java programs can be slow because the String class is inefficient, and because in general the language requires a lot more heap allocated objects than C/C++. File I/O is slow in Java, and of course, a poorly implemented JIT or GC will also make it slow <g>.
Sep 09 2001
parent reply Axel Kittenberger <axel dtone.org> writes:
Walter wrote:

 
 Axel Kittenberger wrote in message <9ngagf$1isq$1 digitaldaemon.com>...
Say java and you'll have to laugh
yourself. I don't know of any others, and I understand the reasons behind
speed penality are to be searched other where, but try to tell this :/

I've worked with Java and gc. GC can make a program faster, given: 1) write code that makes use of gc - for example, in C, people frequently copy strings to avoid memory ownership bugs. With gc, copy a reference, not the data.

I know that, but as said marketing and technics are two different things, just put you're finger on an open project that runs fast and uses GC :/
 3) much temporary generation in C++ can go away, again because of (1).

I guess you mean what the compile generates himself in background, or? Don't know some C++ things are a mystery for myself, and I believe a lot of constructions are wired because they wanted to still use existing linkers. I believe that if the final linker is aware of some language features a lot of optimization and simplification could take place here (like realöy functional inling, or the whole dynamic-type stuff could be done so much simpler if only the final linker would assign IDs, instead of having to output the whole class descriptions into the object file.
 4) Java programs can be slow because the String class is inefficient, and
 because in general the language requires a lot more heap allocated objects
 than C/C++. File I/O is slow in Java, and of course, a poorly implemented
 JIT or GC will also make it slow <g>.

Java programs are slower because they are still interpreted/JIT compiled. However I must hardly defend java here, suns hotspot vm (jdk 1.3) or jre or however their marketing calls it today is the fastest vm I've ever seen, and this goes globally not even for java. It runs blazing fast for having to JIT compile / interpred in background, and one can today even already render graphics in a modern java VM with reasonable speed. - Axel
Sep 10 2001
parent a <a b.c> writes:
Axel Kittenberger wrote:
 
 Walter wrote:

 Java programs are slower because they are still interpreted/JIT compiled.
 However I must hardly defend java here, suns hotspot vm (jdk 1.3) or jre or
 however their marketing calls it today is the fastest vm I've ever seen,
 and this goes globally not even for java. It runs blazing fast for having
 to JIT compile / interpred in background, and one can today even already
 render graphics in a modern java VM with reasonable speed.

Java is a dismal sloth where I work. We had one decrepit machine running several server processes in C. Due to politics we switched to java and now each server has to have its own bleeding edge machine to crawl with insufferable performance. I think it's more than the string implementation. I like what hot java does, but if may echo the words of another, it's a shame we don't do more with self modifying code. Using hot java to make up for java's pathetic performance is as bad as the arguments that have been used to justify micro-kernels. Any optimization hoops you leap backwards through to try to make the micro-kernel tolerable could be done to the monolith to further humiliate the micro-kernel's (lack of) performance. If we could devise smarter systems for optimizing running native code it would help java and native code. I had a bit of hope with Transmeta's work, but it does not seem to be their focus. OK, so most of the arguments against micro-kernels are based on mach which is about as micro as a gas giant. Likewise, java's VM wasn't designed for performance. Well, they didn't get it, and everything since is just an attempted apology. Dan
Sep 10 2001
prev sibling parent Iliya Peregoudov <iliyap mail.ru> writes:
Axel Kittenberger wrote:
 
 Take the class bork that implemnts the interfaces cork and dork. cork and
 dork have 10 functions. Then the tables use at the end:
 dork:   10 entries
 cork:   10 entries
 bork:   20 entries
         --
         50 entries in summeration.
 
 Now with a dispatch table imagined to be flat it would be 20 functions for
 3 classes, so it are 60 entries.

vtable contains entries for all class methods, ie. both inherited and newly defined in the class. Some of the inherited methods can be overriden -- this doesn't increase vtable size. vtable size only increased when new methods added. In current D spec each class also defines an interface. Each class MUST inherit from one class and MAY implement any number of interfaces. Now imagine class A that inherits from B. B is inherits from C and implements D and E. +-----+ +-----+ +-----+ | C | | D | | E | +-----+ +-----+ +-----+ |inher | | +-----+ | | | B |<-impl-/<-impl----+ +-----+ |inher +-----+ | A | +-----+ vtables for class A: vtable1 - contains new A + new B + all D + all E + all C methods, vtable2 - contains all D methods, vtable3 - contains all E methods. vtable1 implements interfaces A, B, C. vtable2 implements D interface. vtable3 implements E interface. vtables for class B: vtable1 - contains new B + all D + all E + all C methods, vtable2 - contains all D methods, vtable3 - contains all E methods. vtable1 implements interfaces B and C. vtable2 implements D interface. vtable3 implements E interface. Lets imagine that vtables for classes C, D, E are trivial -- just one vtable -- classes C, D, E are root classes. Now lets imagine that each class implements 10 new methods and count the size of the tables: E: 10 entries D: 10 entries C: 10 entries B: (vt1=40)+(vt2=10)+(vt3=10)=60 entries A: (vt1=50)+(vt2=10)+(vt3=10)=70 entries all: 160 entries Now try using dispatch tables. Dispatch table for class contains entries for newly defined methods and for overriden methods (not all inherited). So we can calculate minimul and maximum dispatch table sizes. Minimum size (no inherited methods overriden): E: 10 D: 10 C: 10 B: 10 A: 10 all: 50 Maximum size (all iherited methods overriden): E: 10 D: 10 C: 10 B: (inherited=30)+(new=10)=40 D: (inherited=40)+(new=10)=50 all: 120 Actual size is somewhat in the middle (50% of inherited methods are overriden): (120+50)/2=85 This value depends on what percentage of methods are overriden in typical project. For GUI frameworks it typically much less (about 20%). So memory consumption is lesser then for vtables. As I already said another advantage of dispatch tables is that we need only one table per class, not a vtable per each interface class inherits or implements. Implementation of such single-table dispatching is clean and easily understandable. For me, I don't understand how to implement multiple vtables for class. Should it be vtable[INTERFACE_INDEX][METHOD_INDEX]? But any future class can potentially implement any interface, so how to generate consistent INTERFACE_INDEXes?
Sep 10 2001
prev sibling parent reply Axel Kittenberger <axel dtone.org> writes:
 So, why don't use dispatch tables instead of virtual tables?

Dispatch tables have only an advantage for loosly typed languages, or? As far I understood they don't bring any benefits for strong typed languages, where all type conflicts can be determined at compiletime, so the compiler can precalculate all the possible calling situations and thus vtables are more effective. After all for a vtable call you only require to call a function pointer from an array. I searched a little but found nowhere a nice description how dispatch tables actually work :/ - Axel
Sep 08 2001
parent Iliya Peregoudov <iliyap mail.ru> writes:
Axel Kittenberger wrote:
 
 So, why don't use dispatch tables instead of virtual tables?

Dispatch tables have only an advantage for loosly typed languages, or? As far I understood they don't bring any benefits for strong typed languages, where all type conflicts can be determined at compiletime, so the compiler can precalculate all the possible calling situations and thus vtables are more effective. After all for a vtable call you only require to call a function pointer from an array. I searched a little but found nowhere a nice description how dispatch tables actually work :/

A good description of dispatch tables can be found in the "Object-Oriented Programming and the Objective-C Language", Chapter 2, "The Objective-C Language", under "How Messaging Works". This book can be downloaded as PDF file from http://www.gnustep.org/resources/documentation/ObjectivCBook.pdf (480K) And I can't find a nice description how virtual tables work when multiple inheritance come into the scene :/ But (as I understand) it needs multiple virtual tables per class. The advantage of dispatch tables is that we always can put all the things in a single table. Another advantage is when using the language for GUI frameworks. There is no need to invent other meens of events transport -- all events can be mapped to messages. No more Object Pascal dynamic methods bounded to cm_XXX constants, MFC event handler tables, Qt signals -- all is done using intrinsic language features.
Sep 09 2001
prev sibling next sibling parent "Bradeeoh" <bradeeoh crosswinds.net> writes:
I agree with Walter entirely about the enhanced readability and
maintainability of Interfaced code vs M.I.  I also agree with you that in
Java, having to implement entire interfaces is a pain in the butt.
Honestly, how many times do you extend a Frame, make it be it's own
WindowListener, stub out all the methods but windowClosing() and have that
be system exit?

All the other stubs in there can be a PAIN.

So, I offer up a solution.  I've been lax in keeping up with the newsgroup
lately, so forgive me if this has been suggested, but how about this -
Provide a compiler switch that, if a class implements an interface but does
not actually implement each of the required functions of the interface,
causes the compiler to automatically insert empty stubs for the
non-implemented functions.

The issue of functions that actually return values comes up.  Again pointing
to Java experience, I find that interfaces with purly/mostly void functions
(ie, EventListeners), I stub out all but one or two.  Interfaces with
functions that actually return values I tend to implement entirely, though
it depends on the application, of course.

So this compiler switch could stub out void methods, obviously, and perhaps
it could have value returning methods simply return the default value - ie,
Object returns could be "null", integer returns could be 0, booleans be
false.  (true could be up for debate, or be another compiler switch)

The void obviously isn't a problem, and once again I point out that
interface functions that actually return values tend to get meaninful
implementations anyway.  The coder would either implement them knowing that
it's the right thing to do, or if he truly doesn't care and he knows the
language well enough to use this compiler switch, he understands the
consequences of the "auto-stub" function making his code return default
values.

Anyways, just a thought.  I'd love to hear everyone's opinions.

-Brady



"Axel Kittenberger" <axel dtone.org> wrote in message
news:9mst9n$2o41$1 digitaldaemon.com...
 Yu Qian Zhou wrote in message ...
BTW, Walter, have you decided to introduce any better approach into D
other than Java's single inheritance + interface + "Copy&Paste" ?

No, I haven't. I understand that not having MI will result in some code duplication. I don't think, however, it is a cost that exceeds the


 understandability of non-MI code.

I have to agree with Yu Qian, I've programmed some GUI's and java and sometimes you've objects which have implement several interfaces, but

 only a single function of each. A good example are the 'Listeners' java
 has. A listener would be an event slot in qt language. You get all the GUI
 actions into there. Example were something like KeyboardListener,
 MouseListener, etc. Suprisingly the java API always provides each listener
 twice, once as an interface, and once as a normal class you can inherit.

 the situation allows it you can inherit the Listener class and just
 overload the functions you want, but in some situations you alread inherit
 from something else. Then you've to implement the interface and write code
 for -every- function it provides. This is the code duplication how I think
 Yu Qian tried to express.

 In my eyes somebody thought simple single inheritence was enough. Sounds
 like a good idea. But in pratice they soon discovered that there are cases
 it does not suffice, like in many cases one often seen example in java is
 'Application' and 'Runable'. Okay you've to inherit for an Application to
 be start able. If you want to run another thread you've to implement the
 'Runable' interface, because you can't overload also the 'Thread' class
 since you are already an application. In this dilemma the interfaces were
 introduced, a suitable workaround. But exactly in this example writing
 threaded code by inheriting 'Thread' is far more convient than

 the 'Runable' interface which requires you to write more code, and to
 create an additional thread object as 'branch'.

 I think single inheritence was an expiriment, worth taking altough. But
 looking at the results my impression is it failed. Why can't I just

 both from 'Application' and from 'Thread' at the same time? I don't think
 MI is any bad if you implement it stronger typed than C++ did. A function
 is eitherway absolute unique identifyable or a compile error will be
 raised. The worst case that can happen is that the user has to specify

 which parent he wants to call the function Foo() if both should have them.

 Another aspect which make C++ inheritance complicated is public, protected
 and private inheritance. Something I never understood what the real
 benefits of it are. In the sense IS-A and HAS-A, I can hide what I have,
 but I should not be able to hide what I am.

 Diamond inheritance? If it makes problems, just leave it away. You're

 far more powerfull than single inheritence would provide.

 - Axel

Sep 02 2001
prev sibling next sibling parent Charles D Hixson <charleshixsn earthlink.net> writes:
Axel Kittenberger wrote:

 Yu Qian Zhou wrote in message ...
BTW, Walter, have you decided to introduce any better approach
into D other than Java's single inheritance + interface +
"Copy&Paste" ?

No, I haven't. I understand that not having MI will result in some code duplication. I don't think, however, it is a cost that exceeds the greater understandability of non-MI code.

I have to agree with Yu Qian, I've programmed some GUI's and java and sometimes you've objects which have implement several interfaces, but often only a single function of each. A good example are the 'Listeners' java has. A listener would be an ... Diamond inheritance? If it makes problems, just leave it away. You're still far more powerfull than single inheritence would provide. - Axel

Eiffel style multiple-inheritence is impractical, then I would plump for the kind of delegation that Jamie used (Jamie was/is a preprocessor for Java). You could also look at Kiev for some solutions. But Kiev is a complier for the JVM, where Jamie is a preprocessor for Java. N.B.: This use is delegation isn't exactly the same as what MS was proposing for J++. I haven't even looked at C#, so maybe they've changed their terminology. (It does mean forwarding calls from one class to another without writing a lot of patchwork, but as Jamie implements it, some calls can be forwarded to one class, and others to another.) At all events, if space is left open for it, delegation can be added during a second iteration of the language. It doesn't need to be present from the start.
Sep 02 2001
prev sibling parent "Angus Graham" <agraham_d agraham.ca> writes:
"Axel Kittenberger" <axel dtone.org> wrote
 I think single inheritence was an expiriment, worth taking altough. But
 looking at the results my impression is it failed. Why can't I just

 both from 'Application' and from 'Thread' at the same time? I don't think
 MI is any bad if you implement it stronger typed than C++ did. A function
 is eitherway absolute unique identifyable or a compile error will be
 raised. The worst case that can happen is that the user has to specify

 which parent he wants to call the function Foo() if both should have them.

 Another aspect which make C++ inheritance complicated is public, protected
 and private inheritance. Something I never understood what the real
 benefits of it are. In the sense IS-A and HAS-A, I can hide what I have,
 but I should not be able to hide what I am.

 Diamond inheritance? If it makes problems, just leave it away. You're

 far more powerfull than single inheritence would provide.

I'm with you Axel. The important question it seems to me is: - what is so confusing about MI? Programmers spend a _lot_ of our time trying to reverse engineer other people's code. MI makes this harder because it's so hard to figure out what a function call does. You see DoTheThing(); and you have to ask, is this a global function? Is it a method? Is it defined here or in a base? Is it overridden? Overloaded? Does it have defaulted arguments? Is it inherited from two different base classes? Is it inherited from two of the same base class? Even AParent::DoTheOtherThing(); doesn't help. Now I'm asking - why is the class specified? Are we avoiding an override from this very class? Are avoiding an override from an intervening base class? Are we selecting among DoTheOtherThing()s available at the same level? Was it just put there as a sort of comment that this is inherited directly from AParent? It seems to me a. getting rid of Virtual Inheritance b. not permitting MI from two classes that define the same method c. not permitting overloading d. not permitting defaulting gets rid of most of the confusion that arises in these cases. Simply dumping MI in order to short-circuit the whole problem seems to me a perfect example of Throwing Out the Baby With the Bathwater. agraham
Sep 02 2001
prev sibling next sibling parent Yu Qian Zhou <yqz comlab.ox.ac.uk> writes:
 The way to do it is to use interfaces:
 
     class Car : Vehicle, Idable
     {
     }
 
 Base classes after the first base class are interface classes.

-Walter

What exactly do you mean? "The way to do it is to use interfaces ... " ... to *introduce* code duplication by Copy&Paste like Java ? But my question is "How to *avoid* ..." ... code duplication in D ? :-) YuQian
Aug 29 2001
prev sibling parent reply "Richard Krehbiel" <rich kastle.com> writes:
Interfaces are fine and dandy, but the problem is, an interface doesn't
carry any *code* with it.  Just an interface spec.

A Java example:

interface A
{
    void DoRight(int a);    // Declares that any class that implements A
                            // will provide DoRight.  No implementation.
}

class Z implements A
{
    void DoRight(int a)
    {
        // Imagine here 160 lines of complex implementation code.
    }
}

class Y implements A
{
    void DoRight(int a)
    {
        // 160 lines of complex implementation code.
        // No matter that it may be *identical* to Z.DoRight...
    }
}

Duplication of source code.  The worst possible thing for maintenance.

A way to solve this in Java is to declare *another* class...

class H
{
    public void DoRight(int a)
    {
        // 160 lines of, well, you know
    }
}

class Z implements A
{
    H helper = new H;
    public void DoRight(int a)
    {
        helper.DoRight(a);
    }
}

class Y implements A
{
    H helper = new H;
    void DoRight(int a)
    {
        helper.DoRight(a);
    }
}

This is less convenient than multiple inheritance.

This could be alleviated if A.DoRight could have a default implementation.
But since an interface cannot declare any instance variables, this may be
somewhat troublesome.

Why not allow interfaces in D to have instance data, and interface methods
to have implementations?  Then it would be as convenient as multiple
inheritance.  Interfaces would still be distinct from classes, so it's not
*quite* M-I.

"Walter" <walter digitalmars.com> wrote in message
news:9mkido$q2i$1 digitaldaemon.com...
 The way to do it is to use interfaces:

     class Car : Vehicle, Idable
     {
     }

 Base classes after the first base class are interface classes.

 -Walter

 -----------------------------------------------------------------

 Yu Qian Zhou wrote in message ...

 Admittedly Java's single inheritance is easy to implement, and simple to
 use.  But it does have some defect which sometimes force a programmer to
 duplicate code by 'copy & paste':

 Suppose I have two classes 'Car' and 'Dog', which *have already inherited*
 from some more important classes 'Vehicle' and 'Animal' respectively
 (let's assume that 'Vehicle' and 'Dog' do not have a common modifiable
 ancestor); now I want both these two classes to implement another
 interface 'Idable', how can I do it?

 Answer:
   Java: Copy & Paste,               code duplication !!!
    C++: Multiple inheritance,    no code duplication
    C++: Textual code inclusion,  no code duplication
 Eiffel: Multiple inheritance,    no code duplication
 Sather: Semantic code inclusion, no code duplication
           Kiev: MI by delegation,        no code duplication

 so what's the solution D can provide?

      D: ???                         code duplication ???

-- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 home.com (personal)
Aug 30 2001
parent reply "Walter" <walter digitalmars.com> writes:
Unfortunately, the helper class is the way to factor out the common code.
Another way is to embed an instance of A as a member of Y and Z. -Walter

"Richard Krehbiel" <rich kastle.com> wrote in message
news:9ml9gr$1844$1 digitaldaemon.com...
 Interfaces are fine and dandy, but the problem is, an interface doesn't
 carry any *code* with it.  Just an interface spec.

 A Java example:

 interface A
 {
     void DoRight(int a);    // Declares that any class that implements A
                             // will provide DoRight.  No implementation.
 }

 class Z implements A
 {
     void DoRight(int a)
     {
         // Imagine here 160 lines of complex implementation code.
     }
 }

 class Y implements A
 {
     void DoRight(int a)
     {
         // 160 lines of complex implementation code.
         // No matter that it may be *identical* to Z.DoRight...
     }
 }

 Duplication of source code.  The worst possible thing for maintenance.

 A way to solve this in Java is to declare *another* class...

 class H
 {
     public void DoRight(int a)
     {
         // 160 lines of, well, you know
     }
 }

 class Z implements A
 {
     H helper = new H;
     public void DoRight(int a)
     {
         helper.DoRight(a);
     }
 }

 class Y implements A
 {
     H helper = new H;
     void DoRight(int a)
     {
         helper.DoRight(a);
     }
 }

 This is less convenient than multiple inheritance.

 This could be alleviated if A.DoRight could have a default implementation.
 But since an interface cannot declare any instance variables, this may be
 somewhat troublesome.

 Why not allow interfaces in D to have instance data, and interface methods
 to have implementations?  Then it would be as convenient as multiple
 inheritance.  Interfaces would still be distinct from classes, so it's not
 *quite* M-I.

 "Walter" <walter digitalmars.com> wrote in message
 news:9mkido$q2i$1 digitaldaemon.com...
 The way to do it is to use interfaces:

     class Car : Vehicle, Idable
     {
     }

 Base classes after the first base class are interface classes.

 -Walter

 -----------------------------------------------------------------

 Yu Qian Zhou wrote in message ...

 Admittedly Java's single inheritance is easy to implement, and simple to
 use.  But it does have some defect which sometimes force a programmer to
 duplicate code by 'copy & paste':

 Suppose I have two classes 'Car' and 'Dog', which *have already


 from some more important classes 'Vehicle' and 'Animal' respectively
 (let's assume that 'Vehicle' and 'Dog' do not have a common modifiable
 ancestor); now I want both these two classes to implement another
 interface 'Idable', how can I do it?

 Answer:
   Java: Copy & Paste,               code duplication !!!
    C++: Multiple inheritance,    no code duplication
    C++: Textual code inclusion,  no code duplication
 Eiffel: Multiple inheritance,    no code duplication
 Sather: Semantic code inclusion, no code duplication
           Kiev: MI by delegation,        no code duplication

 so what's the solution D can provide?

      D: ???                         code duplication ???

-- Richard Krehbiel, Arlington, VA, USA rich kastle.com (work) or krehbiel3 home.com (personal)

Aug 30 2001
parent Dan Hursh <hursh infonet.isl.net> writes:
Walter wrote:
 Unfortunately, the helper class is the way to factor out the common code.

That is assuming that you wrote A and can hence rewrite it as an interface and helper. Convenience (or a lack there of) aside, you are in trouble if the vendor shipped a class.
 Another way is to embed an instance of A as a member of Y and Z. -Walter

At least with the helper (assuming you can do it) you get polymorphism and an error if a new method gets added to the interface. This is a punt. Dan
Aug 30 2001
prev sibling next sibling parent reply Dan Hursh <hursh infonet.isl.net> writes:
	I believe the D way is code duplication.  I personally don't like this,
but it was a design decision in order to prevent the inheritance mess
that C++ owes us an apology for.  I know C++'s syntax and semantics are
rather nasty and I hear it makes the compiler internals a mess so I
can't complain if MI is left out.  I just dislike how some folk pretend
that there isn't a loss of functionality without MI.
	I don't know if D will support MI since there has been a strong
reaction against, but if you know of a better way that it could be
implemented without sacrificing the other design goals of D (including
an easy to implement compiler), I love to hear it.  Since the time that
I only knew fortran 77 and Commodore basic, I've want MI functionality. 
I just didn't know what it was called until I learn C++.

Dan


Yu Qian Zhou wrote:
 
 Admittedly Java's single inheritance is easy to implement, and simple to
 use.  But it does have some defect which sometimes force a programmer to
 duplicate code by 'copy & paste':
 
 Suppose I have two classes 'Car' and 'Dog', which *have already inherited*
 from some more important classes 'Vehicle' and 'Animal' respectively
 (let's assume that 'Vehicle' and 'Dog' do not have a common modifiable
 ancestor); now I want both these two classes to implement another
 interface 'Idable', how can I do it?
 
 Answer:
           Java: Copy & Paste,               code duplication !!!
            C++: Multiple inheritance,    no code duplication
            C++: Textual code inclusion,  no code duplication
         Eiffel: Multiple inheritance,    no code duplication
         Sather: Semantic code inclusion, no code duplication
           Kiev: MI by delegation,        no code duplication
 
 so what's the solution D can provide?
 
              D: ???                         code duplication ???
 
 D borrowed DbC from Eiffel, perhaps it also worth to check the multiple
 inheritance mechanism of Eiffel, which is much cleaner than C++ (by
 renaming and redefinition).
 
 Or if you still feel strongly against MI, maybe you can also consider
 Sather's semantic code inclusion, or Kiev's forward-able delegation?
 
 Let me know your thoughts on this.
 
 YuQian
 
 Sample code:
 /***********************************************************************
   Java: Copy & Paste, code duplication
 ***********************************************************************/
 interface Idable
 {
   public   void setId(int i);
   public   void getId();
 }
 
 class      Car
 extends    Vehicle
 implements Idable
 {
   private  int id;
   public   void setId(int i) { id = i;   }
   public   void getId()      { return i; }
 }
 
 class      Dog
 extends    Animal
 implements Idable
 {
   private  int id;                              // copy
   public   void setId(int i) { id = i;   }      // &
   public   void getId()      { return i; }      // paste!
 }
 
 /***********************************************************************
   Kiev:  Multiple inheritance by delegation, no code duplication
 ***********************************************************************/
 class Id
 {
 private int id;
 public  void setId(int i) { id = i;   }
 public  void getId()      { return i; }
 }
 
 class   Car
 extends Vehicle
 {
     forward public Id myId;   // forward car.getId() to car.myId.getId()
 }
 
 class   Dog
 extends Animal
 {
     forward public Id myId;   // forward dog.getId() to dog.myId.getId()
 }
 
 /***********************************************************************
   C++:  Multiple inheritance, no code duplication
 ***********************************************************************/
 class Idable
 {
 private: int id;
 public:
    void setId(int i) { id = i;   }
    void getId()      { return i; }
 };
 
 class Car: public Vehicle, Idable {};
 class Dog: public Animal,  Idable {};
 
 /***********************************************************************
   C++:  Textual code inclusion, no code duplication
 ***********************************************************************/
 
 //--- begin file: id.ci ---
 private: int id;
 public:
    void setId(int i) { id = i;   }
    void getId()      { return i; }
 //--- end   file: id.ci ---
 
 class Car: public Vehicle
 {
 #include "id.ci"
 };
 
 class Dog: public Animal
 {
 #include "id.ci"
 };

Aug 29 2001
parent reply Eric Gerlach <egerlach canada.com> writes:
  I believe the D way is code duplication.  I personally don't like this,
 but it was a design decision in order to prevent the inheritance mess
 that C++ owes us an apology for.  I know C++'s syntax and semantics are
 rather nasty and I hear it makes the compiler internals a mess so I
 can't complain if MI is left out.  I just dislike how some folk pretend
 that there isn't a loss of functionality without MI.
 	I don't know if D will support MI since there has been a strong
 reaction against, but if you know of a better way that it could be
 implemented without sacrificing the other design goals of D (including
 an easy to implement compiler), I love to hear it.  Since the time that
 I only knew fortran 77 and Commodore basic, I've want MI functionality. 
 I just didn't know what it was called until I learn C++.

I was thinking about this this morning... you *do* lose a nice feature without MI. The real reason that I think that's the case is because it's a bitch to get it right. There are so may cases of conflicting functions... how do you resolve them and know which function to call? Suppose classes A and B both define foo(). C inherits from both A and B, which one do you inherit? What if foo() in A and B were redefined from a common ancestor, Z? What if one of them is final? There's just too much to think about. Here's my thought: What about allowing MI, as long as there are *no* conflicts? None. Whatsoever. If there are, it's an error. That allows the useful part of MI without getting into the mess. Now, if anyone has a good way of resolving conflicts (I hear Eiffel is good at that) I think we'd all be willing to hear it. But disallowing conflicts shouldn't affect the compiler too much (it just fills in the vtable it was going to leave blank), and it gives ua a nice feature to play with. Comments on that? Eric
Aug 30 2001
next sibling parent Axel Kittenberger <axel dtone.org> writes:
 Now, if anyone has a good way of resolving conflicts (I hear Eiffel is
 good at that) I think we'd all be willing to hear it.  But disallowing
 conflicts shouldn't affect the compiler too much (it just fills in the
 vtable it was going to leave blank), and it gives ua a nice feature to
 play with.
 
 Comments on that?

I agree completly, I had no problems implementing MI for my compiler, I simply forbidded conflict and it I've no problems with it. I don't know why all people damm MI, just because C++ was so a bad implementation of it. In my opinion interfaces are just a workaround around missing MI. I know java puriest will say it isn't, but I believe it is. Especially as in java you've always classes comming in double, as interface and as empty object for inheritince it shows it weakness. Like all the InputListeners, you either can use an default Listener only overriding the functions you're interested in, or you must implement the whole interface sometimes necesarry because you inherit already from something else. Resulting have dozends of meaninlings code lines, just because java wants it so. I think interfaces are just a lame excuse for not having MI. - Axel
Aug 30 2001
prev sibling next sibling parent Dan Hursh <hursh infonet.isl.net> writes:
Eric Gerlach wrote:
 Here's my thought:  What about allowing MI, as long as there are *no*
 conflicts?  None.  Whatsoever.  If there are, it's an error.  That
 allows the useful part of MI without getting into the mess.

Frankly, I find this hyper-idealist. It sounds good, but the cases I care about most is inheriting from class I didn't write. I don't want to rewrite the code as an interface and copy the implementations. I don't want to rewrite them because the both have a toString function. Let me write my own or address the conflict. I suppose I can agree to forcing conflicts to being resolved in the inheriting class so the compiler is left trying to guess, but I would rather have the total deficiency than to have a solution that is too idealized to be of any practical value. Dan
Aug 30 2001
prev sibling parent reply "Sean L. Palmer" <spalmer iname.com> writes:
I've got two words for you.  Virtual and inheritance.

Say you have this situation:

class A
{
}

class B : A
{
}

class C : A
{
}

class D : B, C
{
}

Now how many instances of A are in D?  Two?  Or one... but which one, the
one from B or the one from C?  What if both B and C try to use the same A?
Will it behave ok, or will it be a bug?  Can you tell the compiler which one
to keep, or to merge the two, or to keep both and have duplicate, possibly
conflicting data?  How do clients figure out how to get a pointer to the A
part of a D?

There is runtime overhead for the solution C++ came up with to allow one to
specify that the compiler should merge the A's together.

MI really just opens up a whole can of worms that is just better left
closed.  I've argued the opposite point too, years ago, but lately I really
do agree that if you need MI to do something besides exposing interfaces,
you could most of the time be better off rethinking your class hierarchy
because it's likely flawed.  It's easy to abuse OOP when you start down a
flawed path and to make it work you have to tie up some loose ends... MI
enables both these things, but they're both unnecessary.  MI is appealing
conceptually, until you've had to throw away a few spaghetti projects
because you couldn't disentangle them.

A way to auto generate forwarding functions would be nice though, save a lot
of typing.  Maybe as part of the inheritance mechanism you could say you
want to implement an interface, but have all functionality requests *by
default* be routed to a member object which exposes that same interface.  Of
course you could then override any of them you cared to... that'd enable
some very MI-like objects, but it's all has-a, not is-a;  much less
complicated.

Sean

"Eric Gerlach" <egerlach canada.com> wrote in message
news:3B8E33D8.3060502 canada.com...
  I believe the D way is code duplication.  I personally don't like this,
 but it was a design decision in order to prevent the inheritance mess
 that C++ owes us an apology for.  I know C++'s syntax and semantics are
 rather nasty and I hear it makes the compiler internals a mess so I
 can't complain if MI is left out.  I just dislike how some folk pretend
 that there isn't a loss of functionality without MI.
 I don't know if D will support MI since there has been a strong
 reaction against, but if you know of a better way that it could be
 implemented without sacrificing the other design goals of D (including
 an easy to implement compiler), I love to hear it.  Since the time that
 I only knew fortran 77 and Commodore basic, I've want MI functionality.
 I just didn't know what it was called until I learn C++.

I was thinking about this this morning... you *do* lose a nice feature without MI. The real reason that I think that's the case is because it's a bitch to get it right. There are so may cases of conflicting functions... how do you resolve them and know which function to call? Suppose classes A and B both define foo(). C inherits from both A and B, which one do you inherit? What if foo() in A and B were redefined from a common ancestor, Z? What if one of them is final? There's just too much to think about. Here's my thought: What about allowing MI, as long as there are *no* conflicts? None. Whatsoever. If there are, it's an error. That allows the useful part of MI without getting into the mess. Now, if anyone has a good way of resolving conflicts (I hear Eiffel is good at that) I think we'd all be willing to hear it. But disallowing conflicts shouldn't affect the compiler too much (it just fills in the vtable it was going to leave blank), and it gives ua a nice feature to play with. Comments on that? Eric

Oct 24 2001
parent Jan Knepper <jan smartsoft.cc> writes:
"Sean L. Palmer" wrote:

 I've got two words for you.  Virtual and inheritance.

[ ... snip ... ] AMEN! Jan
Oct 24 2001
prev sibling next sibling parent xiaochuang ling <lingx red.seas.upenn.edu> writes:
On Wed, 24 Oct 2001, Sean L. Palmer wrote:

 enables both these things, but they're both unnecessary.  MI is appealing
 conceptually, until you've had to throw away a few spaghetti projects
 because you couldn't disentangle them.

If you really want to know an elegant MI design, please read this tutorial page: http://www.eiffel.com/doc/online/eiffel50/intro/language/tutorial-10.html#pgfId-515052 It's not a very long page, please do take a few minutes to read, particularly about the keywords :'rename', 'export', 'undefine', 'redefine' and 'select'. And then go back to your questions and see if you're convinced (pay close attention to the section: "Repeated inheritance and selection"). The answer has already been there for years, only if people care to read. BTW, the data persistence machinism of Eiffel is also something I'd like to see in D: 'store' and 'retrieve' the whole object structure starting from a root object to either a disk file or a network connection (by taking into account of reference sharing and circlarity). Even better, the deep_clone gives you a deep copy of the whole object structure in the memory. Hi, Walter, you didn't give much detail about the data persistence machinism of D online. Have you got any plan about it yet? A final question will be, when will the compiler be ready for download? :-)
Oct 26 2001
prev sibling next sibling parent xiaochuang ling <lingx blue.seas.upenn.edu> writes:
 Say you have this situation:

 class A {}
 class B : A {}
 class C : A {}
 class D : B, C {}

 Now how many instances of A are in D?  Two?  Or one... but which one, the
 one from B or the one from C?  What if both B and C try to use the same A?
 Will it behave ok, or will it be a bug?  Can you tell the compiler which one
 to keep, or to merge the two, or to keep both and have duplicate, possibly
 conflicting data?  How do clients figure out how to get a pointer to the A
 part of a D?

I *sincerely* urge you to read the following page to find an clean (and fully programmer-controllable) solution for your questions: http://www.eiffel.com/doc/online/eiffel50/intro/language/tutorial-10.htm l#pgfId-515052
 Suppose classes A and B both define foo().  C inherits from both A and
 B, which one do you inherit?  What if foo() in A and B were redefined
 from a common ancestor, Z?  What if one of them is final?  There's just
 too much to think about.


See above, please read : http://www.eiffel.com/doc/online/eiffel50/intro/language/tutorial-10.htm l#pgfId-515052
 There is runtime overhead for the solution C++ came up with to allow one to
 specify that the compiler should merge the A's together.

 MI really just opens up a whole can of worms that is just better left
 closed.  I've argued the opposite point too, years ago, but lately I really

It's only because C++'s ugly implementation of MI. And because C++ is such a popular language (unfortunately), most people's way of thinking is brain-washing by C++. There are life beyond C++, if you are serious about MI, PLEASE READ: http://www.eiffel.com/doc/online/eiffel50/intro/language/tutorial-10.htm l#pgfId-515052 And check again if your above questions are solved cleanly. P.S. If you do not know Eiffel before, the 'anchored type' is also very interesting, something one can only dreamed of in a C++/Java controlled world.
Oct 27 2001
prev sibling next sibling parent xiaochuang ling <lingx blue.seas.upenn.edu> writes:
Diamond MI:
			A
		       / \
		      /   \
		    B       C
		     \     /
		      \  /
			D

Eiffel's solution:

rename:		change the name of a method

export:		change the accessibility of a method

undefine:	keep the method name and signature, but undefine the
		method body (or in C++'s term, change it to an abstract
		pure virtual func)

redefine:	(in C++: virtual, because Eiffel won't implicitly
		override)

select:		select one of the repeatedly inherited methods.


default behavior:	merged (shared)


So in Eiffel you can even *safely* write:

	class   B
	inherit A  ...
		A  ...
		A  ...
		end


Something you won't able to do in C++.


P.S.
(This is yqz comlab.ox.ac.uk, I'm using my friend's E-mail account.)
Oct 27 2001
prev sibling next sibling parent a <a b.c> writes:
	Frankly, I don't think your idea will make it in, at least not any time
soon.  The decision seems to have already been made.  I am curious what
the `default` behavior actually does?  Merge sounds a bit odd.
	To be honest, it sounds like this could make call forwarding/delegation
very easy.  It's as if the parent classes are almost member data, the
apropriate methods are defined to forward as specified and the class
signature is updated.  It sounds like it could be straight forward
enough.  I still thinkk it won't happen in this language.  There is a
bit of an anti-MI support group going here.
	I'm afraid I could not read the page you gave.  One message had bad
URLs and the other message had a reference to a page that does not
render well in my browser.  It's kind of funny actually.  By the time
you reach the bottom of the page the text is so large that only two
lines will fit on the screen, and the word "Engineering," takes up the
entire width of the screen.

Dan

xiaochuang ling wrote:
 
 Diamond MI:
                         A
                        / \
                       /   \
                     B       C
                      \     /
                       \  /
                         D
 
 Eiffel's solution:
 
 rename:         change the name of a method
 
 export:         change the accessibility of a method
 
 undefine:       keep the method name and signature, but undefine the
                 method body (or in C++'s term, change it to an abstract
                 pure virtual func)
 
 redefine:       (in C++: virtual, because Eiffel won't implicitly
                 override)
 
 select:         select one of the repeatedly inherited methods.
 
 default behavior:       merged (shared)
 
 So in Eiffel you can even *safely* write:
 
         class   B
         inherit A  ...
                 A  ...
                 A  ...
                 end
 
 Something you won't able to do in C++.
 
 P.S.
 (This is yqz comlab.ox.ac.uk, I'm using my friend's E-mail account.)

Oct 27 2001
prev sibling next sibling parent xiaochuang ling <lingx blue.seas.upenn.edu> writes:
 	I'm afraid I could not read the page you gave.

First go to : http://www.eiffel.com/doc/online/eiffel50/intro/language/tutorial-00.html Then click on Section 9.
Oct 27 2001
prev sibling next sibling parent a <a b.c> writes:
xiaochuang ling wrote:
 
       I'm afraid I could not read the page you gave.

First go to : http://www.eiffel.com/doc/online/eiffel50/intro/language/tutorial-00.html Then click on Section 9.

Same problem. I must have an out of date version of netscape. Dan
Oct 27 2001
prev sibling parent xiaochuang ling <lingx red.seas.upenn.edu> writes:
 Same problem.  I must have an out of date version of netscape.

Even with the latest Netscape, you may still have problem :-) Many web-pages these days only *support* (I mean it) M$'s IE. On the other hand, Netscape is quite buggy. Maybe you can try opera, if you don't like the fatty IE. http://www.opera.com/ It's small, fast, up-to-date, FREE; and runs on both Win32 and Linux. (Sorry, this is off-topic on this D-list.)
Oct 27 2001