www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - [Understanding] Classes and delegate inheritance vs function pointers

reply Q. Schroll <qs.il.paperinik gmail.com> writes:
Say I have a class hierarchy like this:
   class Base { }
   class Derived : Base { }
A Derived object cannot be referenced as a Base object, but as a 
const(Base) object. That makes sense to me.

One can replace Base by a  system delegate type (SysDG) and 
Derived by a  safe delegate type (SafeDG) and it works the same 
way: a SafeDG object cannot be referenced as a SysDG object, but 
as a const(SysDG) object.

However, if I try that with function pointers instead of 
delegates (SysFP, SafeFP), for some reason, a SafeFP cannot be 
referenced as a const(SysFP).
This makes no sense in my head. Is there some reason I'm unable 
to see?

Example code is here: https://run.dlang.io/is/zSNArx
Jan 09
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2021-01-09 19:16, Q. Schroll wrote:
 Say I have a class hierarchy like this:
    class Base { }
    class Derived : Base { }
 A Derived object cannot be referenced as a Base object, but as a 
 const(Base) object. That makes sense to me.
It can: Base b = new Derived();
 One can replace Base by a  system delegate type (SysDG) and Derived by a 
  safe delegate type (SafeDG) and it works the same way: a SafeDG object 
 cannot be referenced as a SysDG object, but as a const(SysDG) object.
 
 However, if I try that with function pointers instead of delegates 
 (SysFP, SafeFP), for some reason, a SafeFP cannot be referenced as a 
 const(SysFP).
 This makes no sense in my head. Is there some reason I'm unable to see?
 
 Example code is here: https://run.dlang.io/is/zSNArx
Is there a reason all you're examples are using pointers? Classes are already reference types, delegates are kind of like reference types. They consist of a context pointer and a function pointer. Function pointer are, as the name suggest, already pointers. -- /Jacob Carlborg
Jan 09
parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Saturday, 9 January 2021 at 20:00:35 UTC, Jacob Carlborg wrote:
 On 2021-01-09 19:16, Q. Schroll wrote:
 Say I have a class hierarchy like this:
    class Base { }
    class Derived : Base { }
 A Derived object cannot be referenced as a Base object, but as 
 a const(Base) object. That makes sense to me.
It can: Base b = new Derived();
That's not what I mean. You copy the reference. That's not what referencing meant. Derived d = new Derived(); Base* bp = &d; // fails const(Base) cbp = &d; // compiles.
 Is there a reason all you're examples are using pointers?
Yes. Actually, I need it for slices, but only pointer part of it really mattered. A Derived[] is implicitly a const(Base)[], not a Base[]. A void delegate() safe[] is implicitly a const(void delegate())[]. But it seems a void function() safe[] **isn't** implicitly a const(void function())[]. Functions taking those are kind of useless like that.
Jan 09
next sibling parent reply sighoya <sighoya gmail.com> writes:
On Saturday, 9 January 2021 at 20:20:38 UTC, Q. Schroll wrote:
 That's not what I mean. You copy the reference. That's not what 
 referencing meant.

   Derived d = new Derived();
   Base* bp = &d; // fails
   const(Base) cbp = &d; // compiles.
Generally, allowing covariant assignment for the Ptr<T>, i.e. T*, type only in case of const T* strikes me. IMHO, both should be synchronously (dis)allowed. Because D doesn't support co(ntra)variance, it should be both disallowed? Otherwise, because D already support the const case, why not the non const case? Are there any alignment issues supporting the non const case?
Jan 09
parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Saturday, 9 January 2021 at 21:57:43 UTC, sighoya wrote:
 On Saturday, 9 January 2021 at 20:20:38 UTC, Q. Schroll wrote:
 That's not what I mean. You copy the reference. That's not 
 what referencing meant.

   Derived d = new Derived();
   Base* bp = &d; // fails
   const(Base) cbp = &d; // compiles.
Generally, allowing covariant assignment for the Ptr<T>, i.e. T*, type only in case of const T* strikes me. IMHO, both should be synchronously (dis)allowed.
I'm unsure what you mean. If you have an array `cars` of type Car[], you can treat it as a const(Vehicle)[] because reading a Car as a Vehicle is unproblematic. But if it could bind to Vehicle[], its elements could be written arbitrary Vehicles. Say it worked, then: Car[] cars = ...; Vehicle[] vehicles = cars; vehicles[0] = new Boat; // err... // cars[0] is a Boat now!? Now what? Do what Java does and throw an Exception? Or use const to prevent mutation?
 Because D doesn't support co(ntra)variance, it should be both 
 disallowed?
Imagine for a moment, `const` were named `read_only` and there were a specifier `write_only`. That looks silly, but makes sense in some cases: Vehicle[] vehicles = ...; write_only(Car)[] cars = vehicles; // WTF!? cars[i] = new Car; // wait, that's actually okay... Because write_only isn't a thing in D, contravariance is an odd thing to get your a D mind around.
 Otherwise, because D already support the const case, why not 
 the non const case?
The non-const case makes no sense.
 Are there any alignment issues supporting the non const case?
It's not alignment, probably. (Maybe it is, I know almost nothing about alignment, to be honest.) Among other things, in the non-const case, as you call it, the type system cannot guarantee anymore that stuff that is typed some way actually contains those objects. It is one of the major issues Java has and why everyone and their mom tells you not to use Java's arrays and instead use List or similar well-behaved constructs. BTW, my question wasn't about classes at all. I used them as an illustration what works and asked why (seemingly) the same thing doesn't with function pointers.
Jan 09
parent sighoya <sighoya gmail.com> writes:
On Sunday, 10 January 2021 at 00:44:26 UTC, Q. Schroll wrote:

   Car[] cars = ...;
   Vehicle[] vehicles = cars;
   vehicles[0] = new Boat; // err...
   // cars[0] is a Boat now!?

 Now what? Do what Java does and throw an Exception? Or use 
 const to prevent mutation?
Yes, you are right: ``` Derived derived; Base* bp=&derived; *bp=base; derived.method() //ouch ```
 I'm unsure what you mean.
I mean D doesn't support co(ntra)varaince out of the box, but seems allowing them in certain cases increasing the degree of confusion just as it is the case here. I think it would be just better to generally incorporate co(ntra)variance into D's semantic or completely disregard it.
Jan 10
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/9/21 3:20 PM, Q. Schroll wrote:
 Yes. Actually, I need it for slices, but only pointer part of it really 
 mattered.
 
 A Derived[] is implicitly a const(Base)[], not a Base[].
 A void delegate()  safe[] is implicitly a const(void delegate())[].
 But it seems a void function()  safe[] **isn't** implicitly a const(void 
 function())[].
 
 Functions taking those are kind of useless like that.
It should work. The reason const works is under the theory that you cannot change it, so there is no danger of replacing the value with something that isn't the original type. I would say as long as it's covariant (or contravariant? I can't remember), implicit casting to a const array should work. -Steve
Jan 10
prev sibling parent sighoya <sighoya gmail.com> writes:
On Saturday, 9 January 2021 at 18:16:04 UTC, Q. Schroll wrote:
 This makes no sense in my head. Is there some reason I'm unable 
 to see?
And yes, I think it should work likewise for function pointers, too.
Jan 10