POV-Ray : Newsgroups : povray.off-topic : Generics annoyance Server Time
4 Sep 2024 13:20:52 EDT (-0400)
  Generics annoyance (Message 19 to 28 of 38)  
<<< Previous 10 Messages Goto Latest 10 Messages Next 10 Messages >>>
From: Warp
Subject: Re: Generics annoyance
Date: 14 Apr 2010 12:47:26
Message: <4bc5f19e@news.povray.org>
Nicolas Alvarez <nic### [at] gmailcom> wrote:
> Warp wrote:
> >   I really can't understand what would be the problem in supporting
> > full-fledged multiple inheritance.
> > 
> >   If your answer contains the term "diamond inheritance", then forbid
> > diamond inheritance, not multiple inheritance.

> If you add multiple inheritance to a language where every class inherits 
> from a common base class (Object), every time you multiple-inherit you're 
> getting a diamond.

  Then don't make every class inherit from a common base class. Interfaces
don't, so why should classes?

  If you really want must have classes inherit from Object, then simply
allow interfaces to have member variables and function implementations.
Then you'll have full-fledged multiple inheritance without diamond
inheritance.

-- 
                                                          - Warp


Post a reply to this message

From: Darren New
Subject: Re: Generics annoyance
Date: 14 Apr 2010 12:49:45
Message: <4bc5f229$1@news.povray.org>
Warp wrote:
>   I really can't understand what would be the problem in supporting
> full-fledged multiple inheritance.

Complexity, perhaps.

Also, remember the CIL is designed to support multiple languages. Granted, 
you're going to have trouble accessing objects from COBOL, but it's likely 
that MI would just make it that much more difficult. There's already kludgey 
work-arounds for (e.g.) getting to properties and indexers from languages 
that don't have properties.

>   If your answer contains the term "diamond inheritance", then forbid
> diamond inheritance, not multiple inheritance.

Impossible if everything inherits from Object. :-)

>   (Could this be, perhaps, related to garbage collection? It makes GC a lot
> more complicated if references don't always point to the beginning of the
> allocated memory block?)

That's certainly part of it, but you don't need to support multiple 
inheritance in the same way C++ does it.  Eiffel has multiple inheritance, 
garbage collection, and O(1) virtual dispatch.  (That last would probably be 
quite difficult if you didn't compile everything at the same time, of course.)

C# allows you to inherit from multiple interfaces that define the same 
method names and distinguish between them at compile time and runtime. 
(I.e., you can already have a Dog::Run interface and a Thread::Run interface 
and invoke them that way and have different implementations.) So it's not 
just a problem with the metadata and naming. (Eiffel has a different 
solution to this problem.)

Some of the rules of the language are based on superclasses. If you throw an 
object, it has to descend from Exceptions. A class with value semantics has 
to descend from "Values" or something like that. A delegate (like an OO 
function pointer) has to descend from Delegate. Etc. Maybe that's part of 
the problem.

But really, I don't think there are any obvious technical problems that 
couldn't be solved with enough work.

-- 
Darren New, San Diego CA, USA (PST)
   Yes, we're traveling together,
   but to different destinations.


Post a reply to this message

From: Darren New
Subject: Re: Generics annoyance
Date: 14 Apr 2010 13:13:21
Message: <4bc5f7b1$1@news.povray.org>
Warp wrote:
>   Then don't make every class inherit from a common base class. Interfaces
> don't, so why should classes?

Because then you can put routines on that common base class that are common 
to every class, like ToString() and Equals() and HashCode() and such.

>   If you really want must have classes inherit from Object, then simply
> allow interfaces to have member variables and function implementations.
> Then you'll have full-fledged multiple inheritance without diamond
> inheritance.

That certainly sounds like a good idea to me. :-)

Of course, the way it works now is that an interface doesn't actually change 
what's in the class. It's more a promise that the class implements a 
particular set of routines. So it's not unlike a pure abstract class. If you 
already implement a routine called Run, whether you inherit from Runnable 
doesn't change what's in your class, only what's in your metadata. So adding 
that feature isn't as much of a no-brainer as it might otherwise be.

On the other hand, maybe it wouldn't be a bad idea to have inheriting from 
an interface actually insert code into your class as necessary. It would 
certainly be a trivial way to make that work in C# at least, if not in other 
languages based on the CIL.

Where have you found multiple inheritance particularly useful in C++? Eiffel 
uses it all the time to good effect in its data structures, but I've never 
really seen anything in C++ that used it, in part maybe because of the way 
it's implemented in C++ with classes nested inside other classes and such 
and not being able to inherit from basic types.

-- 
Darren New, San Diego CA, USA (PST)
   Yes, we're traveling together,
   but to different destinations.


Post a reply to this message

From: Warp
Subject: Re: Generics annoyance
Date: 14 Apr 2010 13:53:24
Message: <4bc60113@news.povray.org>
Darren New <dne### [at] sanrrcom> wrote:
> Because then you can put routines on that common base class that are common 
> to every class, like ToString() and Equals() and HashCode() and such.

  I think that a class for which it doesn't make any sense to have something
like "ToString()" it would be bad design to force it to have it after all.

> On the other hand, maybe it wouldn't be a bad idea to have inheriting from 
> an interface actually insert code into your class as necessary. It would 
> certainly be a trivial way to make that work in C# at least, if not in other 
> languages based on the CIL.

  I wonder if it would make garbage collection more complicated because now
you can have references pointing to the middle of an object rather than the
beginning (and it may be the *only* reference pointing to that object, so
the object must not be collected as long as this reference exists, but it
may be more complicated for the GC engine to know this).

> Where have you found multiple inheritance particularly useful in C++?

  Wherever implementing multiple interfaces (or a base class and one or
more interfaces) is useful in any OO language, in cases where it would
actually be useful if the "interface" methods could have default
implementations, or if it would be useful if the interface provided
auxiliary methods for the derived class to use.

  The problem with "interfaces" is that they are not *helpful* at all.
The don't offer any functionality, any default implementations or any
data. These "interfaces" could often become a lot more helpful if they
actually offered something for the derived class so that it doesn't have
to implement everything from scratch every time.

  I'm not saying that every single program I have written (or even a
significant amount of them) uses multiple inheritance, but it has been
useful sometimes.

-- 
                                                          - Warp


Post a reply to this message

From: Warp
Subject: Re: Generics annoyance
Date: 14 Apr 2010 14:04:57
Message: <4bc603c9@news.povray.org>
Darren New <dne### [at] sanrrcom> wrote:
> That's certainly part of it, but you don't need to support multiple 
> inheritance in the same way C++ does it.  Eiffel has multiple inheritance, 
> garbage collection, and O(1) virtual dispatch.  (That last would probably be 
> quite difficult if you didn't compile everything at the same time, of course.)

  As I have mentioned before, you can cast an object pointer to an
incompatible type assuming that:

  1) Both the source and destination types have a virtual table (in C++ it's
created by making at least one member function virtual; usually at least the
destructor should be in these cases).

  2) The actual object to which this pointer is pointing to has been
multiple-inherited from both the source and destination types (even if
by a very long inheritance chain).

  The dynamic cast checks at runtime whether the casting is ok or not
(if it isn't, it returns a null pointer). If the object is of a type
which has been multiple-inherited from both, the dynamic cast will return
a pointer which will point to the destination type part inside the object.

  Curiously, the code which performs the cast doesn't need to know that
the multiple-inherited class even exists or what it looks like, and the
cast will still work properly.

  I wonder if Eiffel supports this as well.

-- 
                                                          - Warp


Post a reply to this message

From: Darren New
Subject: Re: Generics annoyance
Date: 14 Apr 2010 14:14:09
Message: <4bc605f1$1@news.povray.org>
Warp wrote:
>   I think that a class for which it doesn't make any sense to have something
> like "ToString()" it would be bad design to force it to have it after all.

I think there's a reasonable "ToString()" for every class. There aren't many 
routines in Object, but the ones there are pretty universal. "What type am 
I?"  "What code defines this type?"  "What's my address?"  Stuff like that.

In the CIL, I haven't memorized exactly what's there, and some stuff like 
that might be in other libraries. But it's usually pretty universal.

>   I wonder if it would make garbage collection more complicated because now
> you can have references pointing to the middle of an object rather than the
> beginning (and it may be the *only* reference pointing to that object, so
> the object must not be collected as long as this reference exists, but it
> may be more complicated for the GC engine to know this).

For something managed, I think that would be the wrong way to implement MI. 
It's still just one object, even if it inherits data and code from multiple 
classes.  For example, Python has MI and doesn't have pointers into the 
middle of objects.

You could also do it as a sort of "automatic delegation", where each of the 
multiple parent classes is represented as a separate object on the heap, for 
example, and the child class delegates all the non-overridden calls to the 
parent class. In this case, you'd have no significant overhead if the parent 
class contributed only code (as the JIT would optimize out the additional 
redirection on first invocation).  You might be able to also jit-compile the 
multiple classes and rearrange their code to use different offsets for the 
instance variables when you load the CIL code for a class that has multiple 
parents, for example.

The only reason C++ carries pointers to the middle of objects (as far as I 
can tell) is that they can't rewrite the object code to use different 
offsets in the child class compared to the parent class, and of course 
because it can since there's no memory compaction.

>> Where have you found multiple inheritance particularly useful in C++?
> 
>   Wherever implementing multiple interfaces (or a base class and one or
> more interfaces) is useful in any OO language, in cases where it would
> actually be useful if the "interface" methods could have default
> implementations, or if it would be useful if the interface provided
> auxiliary methods for the derived class to use.

So the "mix in" sort of inheritance. OK.

>   The problem with "interfaces" is that they are not *helpful* at all.
> The don't offer any functionality, any default implementations or any
> data. These "interfaces" could often become a lot more helpful if they
> actually offered something for the derived class so that it doesn't have
> to implement everything from scratch every time.

That would definitely be useful, yes.  Maybe a later version will add it.

Probably right after the folks working on Java do. ;-)

-- 
Darren New, San Diego CA, USA (PST)
   Yes, we're traveling together,
   but to different destinations.


Post a reply to this message

From: Darren New
Subject: Re: Generics annoyance
Date: 14 Apr 2010 14:20:55
Message: <4bc60787$1@news.povray.org>
Warp wrote:
> Darren New <dne### [at] sanrrcom> wrote:
>> That's certainly part of it, but you don't need to support multiple 
>> inheritance in the same way C++ does it.  Eiffel has multiple inheritance, 
>> garbage collection, and O(1) virtual dispatch.  (That last would probably be 
>> quite difficult if you didn't compile everything at the same time, of course.)
> 
>   As I have mentioned before, you can cast an object pointer to an
> incompatible type assuming that:

Right.  I'm not sure what that had to do with what I was saying.

I was trying to say that Eiffel holds only one pointer to any given object, 
and that points to the start of the instance. This way, Eiffel gets simpler 
GC. It also does multiple inheritance in fairly complex ways, and has an 
efficient dispatch to handle it (i.e., no run-time lookups up the 
inheritance chain).

I believe the mechanism relies on knowing at compile time what every class 
in the system will be. You don't get the O(1) lookup in Eiffel if you don't 
have the source to all the classes you inherit from when you compile the 
class. You might not even get it if you don't have the source code for those 
classes when you compile the callers, but I'm not sure.

>   I wonder if Eiffel supports this as well.

Eiffel lets you cast up and down the heirarchy. All methods are semantically 
virtual (even if the compiler optimizes them to direct jumps), so the fact 
that you're casting up and down doesn't make it "invalid". You can't cast a 
reference to an object to a type that isn't in that object's parent class 
heirarchy.

There is one class called "ALL" that everyone inherits from. There is one 
class called "NONE" that inherits from everyone, and which has only one 
instance, called "Null". :-) So NULL can cast to any type, and any type can 
cast to "ALL" (which serves as Object).

-- 
Darren New, San Diego CA, USA (PST)
   Yes, we're traveling together,
   but to different destinations.


Post a reply to this message

From: Darren New
Subject: Re: Generics annoyance
Date: 14 Apr 2010 14:21:59
Message: <4bc607c7$1@news.povray.org>
Darren New wrote:
> I think there's a reasonable "ToString()" for every class. 

Oh, and it's definitely helpful to have a class to which everything else can 
be cast. Imagine C if you didn't have the functionality of void*.

-- 
Darren New, San Diego CA, USA (PST)
   Yes, we're traveling together,
   but to different destinations.


Post a reply to this message

From: Warp
Subject: Re: Generics annoyance
Date: 14 Apr 2010 15:37:30
Message: <4bc6197a@news.povray.org>
Darren New <dne### [at] sanrrcom> wrote:
> For something managed, I think that would be the wrong way to implement MI. 
> It's still just one object, even if it inherits data and code from multiple 
> classes.  For example, Python has MI and doesn't have pointers into the 
> middle of objects.

  That sounds like it would be somewhat inefficient. If the pointer always
points to the beginning of the object, then it would always need to resolve
the offset to the members it wants every time it wants to access them
(because in a multiple-inherited object the members of the base class you
want do not necessarily start from the beginning of the object the pointer
is pointing to, but could start from anywhere).

> You could also do it as a sort of "automatic delegation", where each of the 
> multiple parent classes is represented as a separate object on the heap, for 
> example, and the child class delegates all the non-overridden calls to the 
> parent class.

  That sounds like you would need several steps of indirection in order
to access a base class member using a derived class pointer. And checks
for this would need to be done for *all* accesses, even in situations where
there's no inheritance at all (because the code which tries to access the
object through the pointer cannot know if the object it was given was a
derived object or not, without always checking).

> The only reason C++ carries pointers to the middle of objects (as far as I 
> can tell) is that they can't rewrite the object code to use different 
> offsets in the child class compared to the parent class, and of course 
> because it can since there's no memory compaction.

  It's more efficient when code which handles objects of a certain type
doesn't need to perform any checks to see if the object is really of that
precise type, or whether it's some inherited type. From the point of view
of that code the object looks exactly like if it was exactly of that
precise type (all the member offsets are always the same) regardless of
what the actual type of the object is.

  (Things like virtual inheritance can complicate this a bit, though.)

> Probably right after the folks working on Java do. ;-)

  Well, the Java people added their crippled version of templates to the
language regardless of all the arguments why templates are bad, so maybe
there's hope that MI will also be added, regardless of all the arguments
why it's bad.

  (It has always amused me how in the dawn of Java people constantly
preached how Java was like "C++ done right", yet it seems to lack most
things which are *good* and/or useful in C++, like RAII, destructors,
typedefs, enums, templates, multiple inheritance, handling objects by
value, operator overloading and whatnot.)

-- 
                                                          - Warp


Post a reply to this message

From: Warp
Subject: Re: Generics annoyance
Date: 14 Apr 2010 15:38:38
Message: <4bc619be@news.povray.org>
Darren New <dne### [at] sanrrcom> wrote:
> Darren New wrote:
> > I think there's a reasonable "ToString()" for every class. 

> Oh, and it's definitely helpful to have a class to which everything else can 
> be cast. Imagine C if you didn't have the functionality of void*.

  It's useful mostly if you have some way of casting back to the original
type.

-- 
                                                          - Warp


Post a reply to this message

<<< Previous 10 Messages Goto Latest 10 Messages Next 10 Messages >>>

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.