Thread overview | |||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
January 17, 2002 Multiple inheritance | ||||
---|---|---|---|---|
| ||||
Sorry if this has come up before.. This was all looking good to me until I read one thing: "D classes support the single inheritance paradigm, extended by adding support for interfaces. " For me this relegates D from the level of an improved and tidied up C++ down to the level of Java - i.e. useful for some purposes but without the real power and flexibility of C++. I'm sure some would disagree, but why was this feature missed out of D? I can only assume that the designer(s) of D doesn't use multiple inheritance themselves so decided it was superfluous. As RTTI is part of the language (I assume this means all casts are effectively dynamic_cast's), then I can't see a good reason for this other than wanting to use the super() keyword to call the base class rather than specifying the base class by name. I've also just been reading about the lack of template support, which again is a bit sad. Dave |
January 17, 2002 Re: Multiple inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave Emberton | "Dave Emberton" <demberton-nospam@nospam.digitalworkshop.com> wrote in message news:a2698l$31fo$1@digitaldaemon.com... > For me this relegates D from the level of an improved and tidied up C++ down > to the level of Java - i.e. useful for some purposes but without the real power and flexibility of C++. I'm sure some would disagree, but why was this And without bloat and complexity of C++, I must add... In most cases, interfaces are just enough where you'd use C++-style multiple inheritance. Some rare cases when it is really useful simply don't worth it, IMO. Remember the phrase from the D reference? "If a language can capture 90% of the power of C++ with 10% of its complexity, I argue that is a worthwhile tradeoff." I agree wholeheartedly. > feature missed out of D? I can only assume that the designer(s) of D doesn't > use multiple inheritance themselves so decided it was superfluous. As RTTI is part of the language (I assume this means all casts are effectively dynamic_cast's), then I can't see a good reason for this other than wanting Yes, all object casts are safe - they return null if type doesn't match (see cast.d - some info about internal structure of D classes can be borrowed from there). > to use the super() keyword to call the base class rather than specifying the > base class by name. You can do the latter as well, by the way =) "super" is simply a shorter form. > I've also just been reading about the lack of template support, which again > is a bit sad. Walter says templates will be in sooner or later. The compiler is still in too early stage to judge, anyhow. |
January 17, 2002 Re: Multiple inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pavel Minayev | "Pavel Minayev" <evilone@omen.ru> wrote in message news:a26fj0$3oi$1@digitaldaemon.com... > "Dave Emberton" <demberton-nospam@nospam.digitalworkshop.com> wrote in message news:a2698l$31fo$1@digitaldaemon.com... > And without bloat and complexity of C++, I must add... > In most cases, interfaces are just enough where you'd use C++-style multiple > inheritance. Some rare cases when it is really useful simply don't worth it, IMO. Having spent the last 5 years working on a large project that uses multiple inheritance extensively I have to disagree ;-) But in this case there was a particular reason for it, it's not always that useful I agree. >Remember the phrase from the D reference? > > "If a language can capture 90% of the power of C++ with 10% of its > complexity, I argue that is a worthwhile tradeoff." > > I agree wholeheartedly. Okay - but I don't think that the lack of multiple inheritance makes the language any less complex, because if you don't need it you just don't use it. The only place multiple inheritence causes major problems is when you have diamond inheritence with virtual base classes, and I'd be happy for that to be left out. Dave |
January 17, 2002 Re: Multiple inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave Emberton | "Dave Emberton" <demberton-nospam@nospam.digitalworkshop.com> wrote in message news:a26h9g$5f5$1@digitaldaemon.com... > Okay - but I don't think that the lack of multiple inheritance makes the language any less complex, because if you don't need it you just don't use it. The only place multiple inheritence causes major problems is when you have diamond inheritence with virtual base classes, and I'd be happy for that to be left out. The "is multiple inheritance useful or just a bug" discussion goes back at least 10 years to when it was first proposed to be added to C++. The arguments have surged to and fro ever since <g>. |
January 18, 2002 Re: Multiple inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter | "Walter" <walter@digitalmars.com> wrote in message news:a27dpc$pee$1@digitaldaemon.com... > > The "is multiple inheritance useful or just a bug" discussion goes back at least 10 years to when it was first proposed to be added to C++. The arguments have surged to and fro ever since <g>. If multiple inheritance truly isn't useful, then why have it at all? You've taken the Java approach by being able to inherit from one class and several interfaces, which is kind of "multiple inheritance lite". I think most people will see how being able to inherit from multiple interfaces is useful, so why does anyone think that being able to inherit from multiple implementiations (or partial implementations) of those interfaces is not also useful? And as I said, if you don't want to use multiple inheritance then the fact that the language can do it won't get in your way. It just seems completely unnecessary to me. Surely one of the ways to improve on C++ is to make it more object oriented, not less. Dave |
January 18, 2002 Re: Multiple inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave Emberton | Dave Emberton wrote: > If multiple inheritance truly isn't useful, then why have it at all? You've taken the Java approach by being able to inherit from one class and several interfaces, which is kind of "multiple inheritance lite". I think most people will see how being able to inherit from multiple interfaces is useful, so why does anyone think that being able to inherit from multiple implementiations (or partial implementations) of those interfaces is not also useful? > > And as I said, if you don't want to use multiple inheritance then the fact that the language can do it won't get in your way. It just seems completely unnecessary to me. Surely one of the ways to improve on C++ is to make it more object oriented, not less. According to OOP theory, multiple inheritance is a good idea. But it's very difficult to pull off practically in a compiled language. The heart of the matter (to me) is that if we leave out multiple inheritance, the compiler will be simpler, smaller, less likely to have bugs (because it's easier to debug) and will be available sooner. From a technical sense, let me describe at the difficulties of MI. The first thing to remember is that a class is more or less just a structure with some compiler support that makes member function calls easy. Look at the following classes: class Foo { int i,j; int GetI() { return i; } int GetJ() { return j; } } class FooChild : Foo { int GetJ() { return 0; } } Note that there is one non-virtual member function and one virtual member function. The declaration above is somewhat like this C declaration: struct Foo { int i,j; struct { int (*GetJ)(struct Foo*); } vtable; } int Foo_GetI(struct Foo *this) { return this->i; }; int Foo_GetJ(struct Foo *this) { return this->j; }; struct FooChild { Foo parent_Foo; } int FooChild_GetJ(struct Foo *this) { return 0; }; When you create a "Foo" object, the compiler initializes the "vtable.GetJ" variable to point to "Foo_GetJ". When you create a "FooChild" object, it initializes "parent_Foo.vtable.GetJ" to point to "FooChild_GetJ". The beauty of this setup is that the parent Foo object is right at the beginning of the FooChild; thus any pointer to FooChild can be cast to a pointer to Foo without ever modifying the pointer. And since the Foo object is included whole within the FooChild, then the Foo REALLY IS there...with no special code needed, no changes required in Foo. Now consider what happens when you add a class Bar and derive Baz from Foo and Bar: class Bar { int i; int GetI() { return i*3; }; } class Baz : Foo, Bar { } Now, how do you implement this in C???? This is the closest I can come, but it's really ugly: struct Bar { int i; } int GetI(struct Bar *this) { return this->i*3; } struct Baz { Foo parent_Foo; Bar parent_Bar; } Now what does the compiler do when you try to call Baz::GetI()??? Or access Baz::i??? The answers are ambiguous unless you define complex rules for the language. Also, you can't cast a pointer to Baz to a pointer to Bar without modifying the pointer variable. Things get even worse when you have "diamond inheritance," where the same base class is included twice through two different children: class Fred : Foo {}; class Wilma : Foo {}; class Pebbles : Fred, Wilma {}; If you include two copies of Foo (the one that's part of Fred and the one that's part of Wilma), then you can't say that "Pebbles ISA Foo"...you say that "Pebbles INCLUDESA Foo (two of them)." But if you include only one copy of Foo, then you have screwed up the code in either Fred or Wilma. This is where C++ ends up having virtual classes and such. ugh. The beauty of the Java/D alternative is that you avoid all these complexities. Interfaces can be modeled as structures that are just lists of virtual function pointers. class Barney { int i; int GetI() { return i; } } class Betty { int j; int GetJ(); } interface BettyInt { int GetJ(); } class Bambam : Barney,BettyInt { // inherits i and GetI from Barney Betty betty; int GetJ() { return betty->GetJ(); } } Now this has a very workable implementation: struct Barney { int i; } int Barney_GetI(struct Barney *this) { return this->i; } struct Betty { int j; } int Betty_GetJ(struct Betty *this) { return this->j; } struct BettyInt { int (*GetJ)(void*); } struct Bambam { Barney parent_Barney; Betty betty; } int Bambam_GetJ(struct Bambam *this) { return Betty_GetJ(this->betty); } When you cast a pointer to Bambam into a pointer to Betty, it just takes the GetJ that Bambam has and stores that in the BettyInt. The beauty here is that the one object you inherit doesn't have ANY implementation changes, and the interface is defined from the very beginning to be nothing but a huge vtable. This vastly simplifies the compiler. Now, I'm not arguing that multiple inheritance is structurally bad...but it is difficult to implement for relatively little gain. -- 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))) ] |
January 18, 2002 Re: Multiple inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russ Lewis | Yes. As I understand Walter's reasoning, he's omitting "full" multiple inheritence for practical reasons, not philisophical ones. It's not about whether or not multiple inheritence is useful, it's about developing a language in which you stand a better chance of writing bug-free code and having a compiler which correctly implements that language. As can be seen from the C++ specification, getting all the details for complete multiple inheritence specified is hard enough; but then you've got to write a compiler which correctly implements all of that. Everything is much simplier with just multiple interface inheritence and combined with aggregation/delegation you can usually get the job done. Why add a feature that is really only absolutely required 10% of the time and then is very difficult to get right? If you really must have full multiple inheritence, then C++ is the right tool to use. Dan "Russ Lewis" <spamhole-2001-07-16@deming-os.org> wrote in message news:3C484184.758215B@deming-os.org... > Dave Emberton wrote: > > > If multiple inheritance truly isn't useful, then why have it at all? You've > > taken the Java approach by being able to inherit from one class and several > > interfaces, which is kind of "multiple inheritance lite". I think most people will see how being able to inherit from multiple interfaces is useful, so why does anyone think that being able to inherit from multiple > > implementiations (or partial implementations) of those interfaces is not > > also useful? > > > > And as I said, if you don't want to use multiple inheritance then the fact > > that the language can do it won't get in your way. It just seems completely > > unnecessary to me. Surely one of the ways to improve on C++ is to make it > > more object oriented, not less. > > According to OOP theory, multiple inheritance is a good idea. But it's very > difficult to pull off practically in a compiled language. The heart of the > matter (to me) is that if we leave out multiple inheritance, the compiler will > be simpler, smaller, less likely to have bugs (because it's easier to debug) and > will be available sooner. > > > > From a technical sense, let me describe at the difficulties of MI. > > The first thing to remember is that a class is more or less just a structure > with some compiler support that makes member function calls easy. Look at the > following classes: > > class Foo { > int i,j; > int GetI() { return i; } > int GetJ() { return j; } > } > class FooChild : Foo { > int GetJ() { return 0; } > } > > Note that there is one non-virtual member function and one virtual member function. The declaration above is somewhat like this C declaration: > > struct Foo { > int i,j; > struct { > int (*GetJ)(struct Foo*); > } vtable; > } > int Foo_GetI(struct Foo *this) { return this->i; }; > int Foo_GetJ(struct Foo *this) { return this->j; }; > struct FooChild { > Foo parent_Foo; > } > int FooChild_GetJ(struct Foo *this) { return 0; }; > > When you create a "Foo" object, the compiler initializes the "vtable.GetJ" variable to point to "Foo_GetJ". When you create a "FooChild" object, it initializes "parent_Foo.vtable.GetJ" to point to "FooChild_GetJ". > > The beauty of this setup is that the parent Foo object is right at the beginning > of the FooChild; thus any pointer to FooChild can be cast to a pointer to Foo > without ever modifying the pointer. And since the Foo object is included whole > within the FooChild, then the Foo REALLY IS there...with no special code needed, > no changes required in Foo. Now consider what happens when you add a class Bar > and derive Baz from Foo and Bar: > > class Bar { > int i; > int GetI() { return i*3; }; > } > class Baz : Foo, Bar { > } > > Now, how do you implement this in C???? This is the closest I can come, but > it's really ugly: > > struct Bar { > int i; > } > int GetI(struct Bar *this) { return this->i*3; } > struct Baz { > Foo parent_Foo; > Bar parent_Bar; > } > > Now what does the compiler do when you try to call Baz::GetI()??? Or access > Baz::i??? The answers are ambiguous unless you define complex rules for the > language. > > Also, you can't cast a pointer to Baz to a pointer to Bar without modifying the > pointer variable. Things get even worse when you have "diamond inheritance," > where the same base class is included twice through two different children: > > class Fred : Foo {}; > class Wilma : Foo {}; > class Pebbles : Fred, Wilma {}; > > If you include two copies of Foo (the one that's part of Fred and the one that's > part of Wilma), then you can't say that "Pebbles ISA Foo"...you say that > "Pebbles INCLUDESA Foo (two of them)." But if you include only one copy of Foo, > then you have screwed up the code in either Fred or Wilma. This is where C++ > ends up having virtual classes and such. ugh. > > > > The beauty of the Java/D alternative is that you avoid all these complexities. > Interfaces can be modeled as structures that are just lists of virtual function > pointers. > > class Barney { > int i; > int GetI() { return i; } > } > class Betty { > int j; > int GetJ(); > } > interface BettyInt { > int GetJ(); > } > class Bambam : Barney,BettyInt { > // inherits i and GetI from Barney > Betty betty; > int GetJ() { return betty->GetJ(); } > } > > Now this has a very workable implementation: > > struct Barney { > int i; > } > int Barney_GetI(struct Barney *this) { return this->i; } > struct Betty { > int j; > } > int Betty_GetJ(struct Betty *this) { return this->j; } > struct BettyInt { > int (*GetJ)(void*); > } > struct Bambam { > Barney parent_Barney; > Betty betty; > } > int Bambam_GetJ(struct Bambam *this) { return Betty_GetJ(this->betty); } > > When you cast a pointer to Bambam into a pointer to Betty, it just takes the > GetJ that Bambam has and stores that in the BettyInt. The beauty here is that > the one object you inherit doesn't have ANY implementation changes, and the > interface is defined from the very beginning to be nothing but a huge vtable. > This vastly simplifies the compiler. > > > > Now, I'm not arguing that multiple inheritance is structurally bad...but it is > difficult to implement for relatively little gain. > > -- > 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))) ] > > |
January 18, 2002 Re: Multiple inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russ Lewis | "Russ Lewis" <spamhole-2001-07-16@deming-os.org> wrote in message news:3C484184.758215B@deming-os.org... > According to OOP theory, multiple inheritance is a good idea. But it's very > difficult to pull off practically in a compiled language. The heart of the > matter (to me) is that if we leave out multiple inheritance, the compiler will > be simpler, smaller, less likely to have bugs (because it's easier to debug) and > will be available sooner. Okay - that's what we call engineering, and there's nothing wrong with that. But it seems a shame to limit a language because it would require more effort to write the compiler. The problems you mention have already been solved by C++ compilers, so it's not exactly impossible. No doubt the compiler would also be simpler if we left out contracts and unit tests and other features of D ;-) <snip> > The beauty of this setup is that the parent Foo object is right at the beginning > of the FooChild; thus any pointer to FooChild can be cast to a pointer to Foo > without ever modifying the pointer. And since the Foo object is included whole > within the FooChild, then the Foo REALLY IS there...with no special code needed, > no changes required in Foo. Now consider what happens when you add a class Bar > and derive Baz from Foo and Bar: > > class Bar { > int i; > int GetI() { return i*3; }; > } > class Baz : Foo, Bar { > } > > Now, how do you implement this in C???? This is the closest I can come, but > it's really ugly: > > struct Bar { > int i; > } > int GetI(struct Bar *this) { return this->i*3; } > struct Baz { > Foo parent_Foo; > Bar parent_Bar; > } > > Now what does the compiler do when you try to call Baz::GetI()??? Or access > Baz::i??? The answers are ambiguous unless you define complex rules for the > language. If you mean that both Foo and Bar have GetI() then the same as C++ - causes a compiler error. I don't see how anyone would ever want it to try and work it out. If you mean that Foo doesn't have a GetI, then I guess what the compiler needs to do is something like: Bar* pBar=(Bar*)((char*)pBaz)+sizeof(Foo) Bar_GetI(pBar) > Also, you can't cast a pointer to Baz to a pointer to Bar without modifying the > pointer variable. True, but as all casts are dynamic_casts in D anyway, then a cast is never quite as simple as just using the same pointer value. Whatever mechanism makes that work must be able to make the multiple inheritance case work too. > Things get even worse when you have "diamond inheritance," > where the same base class is included twice through two different children: As I said, I'd be happy for the diamond inheritance to be left out as it really is a bad idea. > Now, I'm not arguing that multiple inheritance is structurally bad...but it is > difficult to implement for relatively little gain. True - but is the goal here to create a better language, or is the goal here to create a simpler compiler? From my point of view it's a shame that it's been left out, but I accept that others may have a completely different opinion. Dave > > -- > 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))) ] > > |
January 18, 2002 Re: Multiple inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave Emberton | Dave Emberton wrote: > Okay - that's what we call engineering, and there's nothing wrong with that. But it seems a shame to limit a language because it would require more effort to write the compiler. The problems you mention have already been solved by C++ compilers, so it's not exactly impossible. No doubt the compiler would also be simpler if we left out contracts and unit tests and other features of D ;-) But that's exactly the point - none (or very few) of the compilers ever got it all right! Rather than have a good standard, C++ is a mixmash of partially incompatible compilers. In truth, to get portable, reliable code, you pretty much have to dump most of the cooler features. > If you mean that both Foo and Bar have GetI() then the same as C++ - causes a compiler error. I don't see how anyone would ever want it to try and work it out. My memory is that actually it *is* possible. If both GetI's are declared as virtual functions, then it is legal. Or do you only have to declare one of them virtual? If so, which one? The point is, very few people know what the standard actually is enough to be able to use it. > As I said, I'd be happy for the diamond inheritance to be left out as it really is a bad idea. Since *all* classes in D are derived from Object, then every instance of multiple inheritance has to deal with diamon inheritance. :( > > Now, I'm not arguing that multiple inheritance is structurally bad...but > it is > > difficult to implement for relatively little gain. > > True - but is the goal here to create a better language, or is the goal here to create a simpler compiler? From my point of view it's a shame that it's been left out, but I accept that others may have a completely different opinion. I'm just not convinced that MI gives you a better language. But I am convinced that having partially-conforming, partially incompatible compilers gives you a worse language. So it seems that MI and stuff like that would be risking almost certain downsides against very small upsides. Now, OTOH, I'm not against defining a separate standard of some sort that takes a language much like D (but uses MI) and translates it down to D. That might be workable, and could almost certainly be written in a portable script format like PERL or something. Maybe we can call it D++ :) -- 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))) ] |
January 18, 2002 Re: Multiple inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to J. Daniel Smith | "J. Daniel Smith" <j_daniel_smith@deja.com> wrote in message news:a29hl2$ka6$1@digitaldaemon.com... > Yes. As I understand Walter's reasoning, he's omitting "full" multiple inheritence for practical reasons, not philisophical ones. It's not about whether or not multiple inheritence is useful, it's about developing a language in which you stand a better chance of writing bug-free code and having a compiler which correctly implements that language. > > As can be seen from the C++ specification, getting all the details for complete multiple inheritence specified is hard enough; but then you've got > to write a compiler which correctly implements all of that. Everything is much simplier with just multiple interface inheritence and combined with aggregation/delegation you can usually get the job done. > > Why add a feature that is really only absolutely required 10% of the time and then is very difficult to get right? If you really must have full multiple inheritence, then C++ is the right tool to use. That's essentially correct. MI is a major implementation effort, but adds only minor incremental functionality to the language. MI has been around for 10 years in C++, and 10 years of experience has not made a compelling case that it's really necessary. Single inheritance handles 90% of the cases, adding interfaces handles 90% of the rest. That leaves 1% <g>. Yes, I do understand that some folks really do like MI and require it for their own style of programming. |
Copyright © 1999-2021 by the D Language Foundation