www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Private and Protected Inheritance.

reply AJG <AJG_member pathlink.com> writes:
Hi there,

I've looked through the archives for info on this but didn't find much. I also
searched the docs, also in vain. My question is whether D supports, will
support, or will never support private and/or protected inheritance. Drawing an
analogy to C++, currently what D does is:

class Foo {}
class Bar : Foo {}
// is technically:
class Bar : public Foo {}
// in other words, public inheritance.

However, private and protected inheritance also exist. They can be very useful,
and would be a very welcome addition. I tried it w/ DMD 0.127, and though
curiously the syntax is legal, it doesn't alter the access level:

class Bar : private Foo {}   // compiles, but still public.
class Bar : protected Foo {} // compiles, but still public.

Note: I am aware about the possibility of casting up the object hierarchy to a
more open level (e.g. cast (Foo) someBar). Perhaps that could be made illegal.
Or not; it doesn't matter that much. The feature is useful regardless. Explicit
casts are always dangerous.

Thanks,
--AJG.
Jul 13 2005
parent reply Mike Capp <mike.capp gmail.com> writes:
In article <db4sko$1qr6$1 digitaldaemon.com>, AJG says...
However, private and protected inheritance also exist. They can be very useful,

Examples? Private inheritance is normally used in C++ to achieve what D does far more cleanly with mixins. As for protected, AFAIK there's no consensus in the C++ community on what it even _means_.
Jul 14 2005
parent reply AJG <AJG_member pathlink.com> writes:
In article <db56ku$277j$1 digitaldaemon.com>, Mike Capp says...
In article <db4sko$1qr6$1 digitaldaemon.com>, AJG says...
However, private and protected inheritance also exist. They can be very useful,

Examples?

Well, say you have a base class with a hefty and complex interface and/or implementation (this doesn't mean badly-designed, though). E.g.: class DeviceContext { // ... uses threads ... // ... and drivers ... // ... and mutexes ... // ... and various kernel objects ... // ... and god knows what else ... } Then you want to create a higher-level object with a more abstract representation. It should be safer too; without a way to screw the system up. E.g.: class Graphics : DeviceContext { // Provides things like: DrawCircle(); DrawSquare(); DrawTriangle(); // etc... } So, you can tell your users (or yourself) "use the Graphics class if you don't want to see the gory hardware details." Yet with public inheritance, which is the only option right now, a Graphics instance doesn't shield you against the DeviceContext. You can still mess things up at the lower level by calling inappropriate functions and setting dangerous properties. Therefore, wouldn't it be nice to have the requested inheritance facility? Note: Yes, it _is_ possible to create a private instance of DeviceContext within Graphics and then work from there. But this is sub-optimal because of various reasons: 1) Graphics would not be able to extend DeviceContext. 2) Graphics has limited access to DeviceContext. All protected members are gone. 3) If DeviceContext was meant to be derived from, or is simply abstract, then you need to create an intermediate class -hardly an elegant solution. At any rate, I don't think this feature takes away anything from the language and wouldn't conflict with any current code. In fact, a I said, I believe the syntax is already valid. Could this be given some consideration? Thanks, --AJG.
Private inheritance is normally used in C++ to achieve what D does far more
cleanly with mixins. As for protected, AFAIK there's no consensus in the C++
community on what it even _means_.

I don't think this should be too complicated. Members that would have been made private (from public) are made protected instead.
Jul 14 2005
next sibling parent reply Mike Capp <mike.capp gmail.com> writes:
In article <db61hd$2vek$1 digitaldaemon.com>, AJG says...
Well, say you have a base class with a hefty and complex interface and/or
implementation (this doesn't mean badly-designed, though). E.g.:

[snip DeviceContext class]

Then you want to create a higher-level object with a more abstract
representation. It should be safer too; without a way to screw the system up.

[snip Graphics class derived from DeviceContext]
So, you can tell your users (or yourself) "use the Graphics class if you don't
want to see the gory hardware details." Yet with public inheritance, which is
the only option right now, a Graphics instance doesn't shield you against the
DeviceContext.

You can still mess things up at the lower level by calling inappropriate
functions and setting dangerous properties. Therefore, wouldn't it be nice to
have the requested inheritance facility?

But... but... isn't this exactly what interfaces are for? Define an IGraphics interface with DrawCircle(), DrawSquare() etc, have your Graphics class implement that interface, and give an IGraphics to client code instead of a Graphics.
As for protected, AFAIK there's no consensus in the C++
community on what it even _means_.

I don't think this should be too complicated. Members that would have been made private (from public) are made protected instead.

I meant "what it means semantically", rather than what the syntax looks like. Public inheritance means "Derived is substitutable for Base". Private inheritance means "Derived reuses code from base without exposing that fact in its interface". What does protected inheritance mean? cheers Mike
Jul 14 2005
parent reply AJG <AJG_member pathlink.com> writes:
Hi there,

But... but... isn't this exactly what interfaces are for? Define an IGraphics
interface with DrawCircle(), DrawSquare() etc, have your Graphics class
implement that interface, and give an IGraphics to client code instead of a
Graphics.

This is yet another incomplete way to solve the problem. 1) This introduces redundancy (rarely good in code), in vain. You must define the interface and then implement it, maintaining the same thing twice, despite the fact that there will likely only ever be one implementation. I understand the use of interfaces for situations where at least they'll be useful multiple times, but this is hardly the case. 2) Graphics (and thus, DeviceContext) are now essentially sealed. Neither can be extended. I guess you could inherit from IGraphics and then use that, but see point 1. Even then, the actual implementations would be locked and unavailable for reuse. 3) Also, what do you mean give an IGraphics? You can't just give an IGraphics, it would be useless to users. At some point, they're going to need to initialize a _real_ Graphics and use that. Then you get the whole problem back again, since DeviceContext is exposed anew. The pattern I see is that the interface approach is usually useful only when the implementation is not relevant and not required. In this case, the implementation is important, so it doesn't work well. I want to use and keep the implementation available through the object hierarchy, but with the _right_ access modifier (hence, the need for private).
I meant "what it means semantically", rather than what the syntax looks like.
Public inheritance means "Derived is substitutable for Base". Private
inheritance means "Derived reuses code from base without exposing that fact in
its interface". What does protected inheritance mean?

I'll take a shot: "Derived reuses code from base without exposing that fact in its interface, while keeping the interface open for subclassing." Cheers, --AJG.
Jul 15 2005
parent Mike Capp <mike.capp gmail.com> writes:
In article <dba19s$mqs$1 digitaldaemon.com>, AJG says...
But... but... isn't this exactly what interfaces are for?

1) This introduces redundancy (rarely good in code), in vain. You must define the interface and then implement it, maintaining the same thing twice, despite the fact that there will likely only ever be one implementation.

It's not a major burden in practice. The big risk with redundancy is things getting out of sync, and the compiler will let you know if this happens with interfaces and their implementations. Maybe we're coming from different places; I'm mostly doing agile C# development these days, and interfaces almost always have at least one "other" implementation, even if it's only a mock or stub for unit-testing client code.
2) Graphics (and thus, DeviceContext) are now essentially sealed. Neither can 
be extended. 

Why not? # interface IGraphics { void DrawSquare(); } # class Graphics : IGraphics { void DrawSquare() {} } # # // Dammit, now I need to draw teapots too. OK... # # interface IMyGraphics : IGraphics { void DrawTeapot(); } # class MyGraphics : Graphics, IMyGraphics { void DrawTeapot() {} }
3) Also, what do you mean give an IGraphics? You can't just give an IGraphics,
it would be useless to users. 

I mean that while you're probably givin them a Graphics *object*, the argument and/or retval types they see will be IGraphics. They don't know that it's a Graphics, so they can't see any of the implementation bits they shouldn't.
I'll take a shot: "Derived reuses code from base without exposing that fact in
its interface, while keeping the interface open for subclassing."

In practice, I've found that (for C#/Java) interfaces work fine. For C++ you'd normally either use composition or turn things around and have DeviceContextGraphics (implementation) derive from Graphics (interface), using something like the GoF Template Method pattern and/or the "Non Virtual Interface" idiom (see e.g. http://www.gotw.ca/publications/mill18.htm) cheers Mike
Jul 16 2005
prev sibling next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"AJG" <AJG_member pathlink.com> wrote in message 
news:db61hd$2vek$1 digitaldaemon.com...
 I don't think this should be too complicated. Members that would have been 
 made
 private (from public) are made protected instead.

Wouldn't that defeat the entire purpose of having the 'private' protection attribute? It means "cannot be seen by ANYTHING, including derived classes." If derived classes can override it, then it's no different than 'protected.'
Jul 14 2005
parent reply AJG <AJG_member pathlink.com> writes:
Hi,

 I don't think this should be too complicated. Members that would have been 
 made
 private (from public) are made protected instead.

Wouldn't that defeat the entire purpose of having the 'private' protection attribute? It means "cannot be seen by ANYTHING, including derived classes." If derived classes can override it, then it's no different than 'protected.'

No, no, what I meant is this: class Foo { public int A; } class Bar : public Foo { /* Derived A is still public. */ } class Bar : private Foo { /* Derived A is now private. */ } class Bar : protected Foo { /* Derived A is now protected. */ } In other words, the members that were originally (in the base class) public, but would have been made private when using private inheritance, would be made protected instead (when using protected inheritance). Is that better? Cheers, --AJG.
Jul 14 2005
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"AJG" <AJG_member pathlink.com> wrote in message 
news:db6qee$jmf$1 digitaldaemon.com...
 Is that better?

Much, thanks for the explanation. However, private and protected inheritance seem like very "niche" and complex features for very little benefit. In addition, it might not be possible to implement well in D, as a private function cannot be virtual - and changing a function's type from public to private when inherited wouldn't fly, given D's inbuilt "virtual" determination...
Jul 14 2005
next sibling parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
Jarrett Billingsley wrote:
 "AJG" <AJG_member pathlink.com> wrote in message 
 news:db6qee$jmf$1 digitaldaemon.com...
 
Is that better?

Much, thanks for the explanation. However, private and protected inheritance seem like very "niche" and complex features for very little benefit. In addition, it might not be possible to implement well in D, as a private function cannot be virtual - and changing a function's type from public to private when inherited wouldn't fly, given D's inbuilt "virtual" determination...

I don't know how hard would it be to implement, but I think there is a better way around it: class A { /* stuff */ } class B { private A a; /* other stuff */ } I think that makes more sense, because if you want to hide A's interface then B probably is not an A, it just uses it. Even the DeviceContext example, Graphics probably _uses_ a device context to draw stuff, but it's not really a device context. Ofcourse I'm just making assumptions here, but my feeling is if you need to hide the super classes interface then you probably are not of the same type as the super class.
Jul 14 2005
next sibling parent "Uwe Salomon" <post uwesalomon.de> writes:
 Ofcourse I'm just making assumptions here, but my feeling is if you need  
 to hide the super classes interface then you probably are not of the  
 same type as the super class.

Exactly. Ciao uwe
Jul 14 2005
prev sibling parent AJG <AJG_member pathlink.com> writes:
Hi there,

I don't know how hard would it be to implement, but I think there is a 
better way around it:

class A { /* stuff */ }

class B { private A a; /* other stuff */ }

I think that makes more sense, because if you want to hide A's interface 
then B probably is not an A, it just uses it.

Even the DeviceContext example, Graphics probably _uses_ a device 
context to draw stuff, but it's not really a device context.
Ofcourse I'm just making assumptions here, but my feeling is if you need 
to hide the super classes interface then you probably are not of the 
same type as the super class.

Yes, I mentioned that this is a possibility, but it's not ideal. It's lacking in functionality because: 1) Graphics (B) would not be able to extend or override DeviceContext (A). 2) Graphics (B) has limited access to DeviceContext (A). All protected members are gone. 3) If DeviceContext (B) was meant to be derived from, or is simply abstract, then you need to create an intermediate class -hardly an elegant solution. In essence, there is a marked difference between private containment (your suggestion), which is also known as "HAS-ONE-OF," and private inheritance, aka "IS-IMPLEMENTED-IN-TERMS-OF." It's incorrect to assume that just because a lower class doesn't want to expose a super class' members it should not be one of the same type. In this case, it's about the API and how you want your object accessed. This is a great facility for that, and makes things simpler. --AJG.
Jul 15 2005
prev sibling parent AJG <AJG_member pathlink.com> writes:
Hi,

Much, thanks for the explanation.

Sure thing.
However, private and protected inheritance seem like very "niche" and 
complex features for very little benefit.  In addition, it might not be 
possible to implement well in D, as a private function cannot be virtual - 
and changing a function's type from public to private when inherited 
wouldn't fly, given D's inbuilt "virtual" determination... 

Can anyone who knows about the compiler enough confirm this? --AJG.
Jul 15 2005
prev sibling parent reply "Andrew Fedoniouk" <news terrainformatica.com> writes:
"AJG" <AJG_member pathlink.com> wrote in message 
news:db61hd$2vek$1 digitaldaemon.com...
 In article <db56ku$277j$1 digitaldaemon.com>, Mike Capp says...
In article <db4sko$1qr6$1 digitaldaemon.com>, AJG says...
However, private and protected inheritance also exist. They can be very 
useful,

Examples?

Well, say you have a base class with a hefty and complex interface and/or implementation (this doesn't mean badly-designed, though). E.g.: class DeviceContext { // ... uses threads ... // ... and drivers ... // ... and mutexes ... // ... and various kernel objects ... // ... and god knows what else ... } Then you want to create a higher-level object with a more abstract representation. It should be safer too; without a way to screw the system up. E.g.: class Graphics : DeviceContext { // Provides things like: DrawCircle(); DrawSquare(); DrawTriangle(); // etc... }

Try this: From Harmonia: --------------------------------------------------- module harmonia.ui.native.win32graphics; class NativeGraphics { package { // ... various native objects ... void nativeDrawRect(rect rc) { .... } } } --------------------------------------------------- module harmonia.gx.graphics; class Graphics: NativeGraphics { void drawRect(rect rc) { nativeDrawRect(rc); } } -------------------------- void testDraw(Graphics g) { g.nativeDrawRect(place); // Error: class harmonia.gx.graphics.Graphics // member nativeDrawRect is not accessible } ---------------------------
Jul 14 2005
parent AJG <AJG_member pathlink.com> writes:
Hi Andrew,

Thanks for your suggestion. It's pretty amazing that the random example I picked
actually turns out to be implemented and it requires the _exact_ thing I was
talking about. I feel validated for some reason.

Indeed the package attribute is of _some_ help and could be hacked in to achieve
similar functionality (to what we need, true private inheritance). There are
various problems though:

1) Package access is semantically confusing at the moment. Nobody's really sure
what it's supposed to do. It's incomplete and unreliable. Or at least:
2) Package access is buggy.
3) Package access is still "friend" access. If you have your classes in the same
file you lose the effect.
4) You need to modify the super class to hack the thing in. What if you can't
edit that class? Why should you have to modify a super class for the behaviour
of some unknown lower class? What if you don't have proper access to the code?
Or, if an ABI eventually matures, what if the source is not even there? For that
you need private inheritance. It's elegant and simple.

As a side note, I checked out the harmonia page and it looks really awesome. If
I understand correctly, there are no OS-widgets at all? That's pretty
impressive.

--AJG.


Try this:

From Harmonia:
---------------------------------------------------
module harmonia.ui.native.win32graphics;

class NativeGraphics
{
   package
   {
      // ... various native objects ...
      void nativeDrawRect(rect rc) { .... }
   }
}
---------------------------------------------------
module harmonia.gx.graphics;

class Graphics: NativeGraphics
{
    void drawRect(rect rc)
    {
      nativeDrawRect(rc);
    }
}

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

void testDraw(Graphics g)
  {
    g.nativeDrawRect(place);
//  Error: class harmonia.gx.graphics.Graphics
//  member nativeDrawRect is not accessible
  }

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

Jul 15 2005