December 04 Re: Struct inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Derek Fawcus | On 04/12/2024 10:37 AM, Derek Fawcus wrote: > On Tuesday, 3 December 2024 at 20:45:32 UTC, Richard (Rikki) Andrew Cattermole wrote: > >>> Would your proposal allow: >>> >>> ```d >>> struct Grandad { >>> } >>> >>> struct Dad : Grandad { >>> } >>> >>> struct Child : Dad { >>> } >>> ``` >>> >>> (I'm not sure if the scheme I mention does, I'd have to reread the specs; but I believe it doesn't). >> >> Yes. > > OK. > > I'd suggest that the member variable version of 'alias this' is also a form of embedding / composition. > > It allows one to control the exact in-memory layout of the struct so defined. Whereas AFAICT, the inheritance suggestion does not. > > Issues/Questions with inheritance suggestion: > > 1. What is the in memory layout? > (Presumably the 'parent' elements simply appear first in memory). Yes, that would be my thought. > 2. Inability to control in memory layout. If you need the control between last field of parent, and first of child, I suspect that this isn't the language feature for you. But if we do need to solve it, we could solve it later on with an attribute. I don't think there is a better way to do it than an attribute, so this can be done later on should we need it, rather than arguing about it now. > 3. What type is passed to a parent struct member function when called on the child. > (Presumably it is the class behaviour of passing the child stuct, with 'isA' relations) > (If not, then there are two different behaviours for inheritance - class vs struct) A class will pass a this pointer typed as its class, not a child. ```d import std.stdio; void main() { Child child = new Child; child.method(); Parent parent = child; parent.method; } class Parent { void method() { writeln(typeof(this).stringof); } } class Child : Parent { override void method() { super.method(); writeln(typeof(this).stringof); } } ``` Will output: ``` Parent Child Parent Child ``` Struct inheritance would have the same output. > 4. What other parts of the class behaviour will apply? > (i.e. will the class implicit mutex lock now be instantiated) > (if not, then classes and structs using inheritance will have different threading behaviour) There is no vtable, no root type (Object class provides the mutex). Therefore unless its stated, no behavior from classes is coming across to structs. Note: classes do not lock the mutex automatically for you. You must specify the synchronized keyword to make it do it. Unless specified by the user, the temporal properties of each match. Existing structs will not change behavior. |
December 04 Re: Struct inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Richard (Rikki) Andrew Cattermole | On Tuesday, 3 December 2024 at 23:18:12 UTC, Richard (Rikki) Andrew Cattermole wrote:
>
Sorry, I had Obj-C in mind when I wrote that, and my use of that was over 10 years ago; hence my confusion.
So what would your scheme do for the struct equivalent of this, assuming it is even compiles:
```d
import std.stdio;
void main() {
Child child = new Child;
child.methB();
writeln();
Parent parent = child;
parent.methB;
}
class Parent {
void methB() {
writeln("MethB(P) ", typeof(this).stringof);
methC();
}
void methC() {
writeln("MethB(P) ", typeof(this).stringof);
}
}
class Child : Parent {
override void methC() {
writeln("MethC(C) ", typeof(this).stringof);
}
}
```
Where the above give this output:
```
$ ldc2 -run class.d
MethB(P) Parent
MethC(C) Child
MethB(P) Parent
MethC(C) Child
```
|
December 04 Re: Struct inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Derek Fawcus | (That was a bit iffy, try this one instead)
On Wednesday, 4 December 2024 at 11:10:59 UTC, Derek Fawcus wrote:
> So what would your scheme do for the struct equivalent of this, assuming it is even compiles:
```D
import std.stdio;
void main() {
Parent parent = new Parent;
parent.methB("Direct");
writeln();
Child child = new Child;
child.methB("Inherit");
writeln();
parent = child;
parent.methB("Assign");
}
class Parent {
void methB(string s) {
writeln("MethB(P) ", s, " ", typeof(this).stringof);
methC(s);
}
void methC(string s) {
writeln("MethC(P) ", s, " ", typeof(this).stringof);
}
}
class Child : Parent {
override void methC(string s) {
writeln("MethC(C) ", s, " ", typeof(this).stringof);
}
}
```
For which I get:
```
MethB(P) Direct Parent
MethC(P) Direct Parent
MethB(P) Inherit Parent
MethC(C) Inherit Child
MethB(P) Assign Parent
MethC(C) Assign Child
```
|
December 05 Re: Struct inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Derek Fawcus | On 05/12/2024 12:38 AM, Derek Fawcus wrote: > For which I get: > > ``` > MethB(P) Direct Parent > MethC(P) Direct Parent > > MethB(P) Inherit Parent Due to no vtables, you cannot override a parent method and have it see the override. MethC(C) Inherit Parent > ``` The last example, assignment, this requires vtables and casting up. Which structs cannot do. |
December 04 Re: Struct inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Richard (Rikki) Andrew Cattermole | On Wednesday, 4 December 2024 at 11:51:57 UTC, Richard (Rikki) Andrew Cattermole wrote:
> On 05/12/2024 12:38 AM, Derek Fawcus wrote:
>> For which I get:
>>
>> ```
>> MethB(P) Direct Parent
>> MethC(P) Direct Parent
>>
>> MethB(P) Inherit Parent
>
> Due to no vtables, you cannot override a parent method and have it see the override.
>
> MethC(C) Inherit Parent
>
>> ```
>
> The last example, assignment, this requires vtables and casting up.
>
> Which structs cannot do.
So are you saying all three would be like the 'Direct Parent' case?
Presumably by making the Assign case cheat (manually forcibly casting a pointer of the child to a pointer of the parent), it would also yield the same result.
i.e.:
```
MethB(P) Direct Parent
MethC(P) Direct Parent
MethB(P) Inherit Parent
MethC(P) Inherit Parent
MethB(P) Assign Parent
MethC(P) Assign Parent
```
Or for the child method override, would you simply yield a compile failure - as it can never work, and has no backward compatibility issue?
|
December 05 Re: Struct inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Derek Fawcus | On 05/12/2024 1:03 AM, Derek Fawcus wrote: > On Wednesday, 4 December 2024 at 11:51:57 UTC, Richard (Rikki) Andrew Cattermole wrote: >> On 05/12/2024 12:38 AM, Derek Fawcus wrote: >>> For which I get: >>> >>> ``` >>> MethB(P) Direct Parent >>> MethC(P) Direct Parent >>> >>> MethB(P) Inherit Parent >> >> Due to no vtables, you cannot override a parent method and have it see the override. >> >> MethC(C) Inherit Parent >> >>> ``` >> >> The last example, assignment, this requires vtables and casting up. >> >> Which structs cannot do. > > So are you saying all three would be like the 'Direct Parent' case? > > Presumably by making the Assign case cheat (manually forcibly casting a pointer of the child to a pointer of the parent), it would also yield the same result. Casting a pointer to the parent, would be ``@system``, so what it does is very much "good luck with that". > i.e.: > > ``` > MethB(P) Direct Parent > MethC(P) Direct Parent > > MethB(P) Inherit Parent > MethC(P) Inherit Parent > > MethB(P) Assign Parent > MethC(P) Assign Parent > ``` > > Or for the child method override, would you simply yield a compile failure - as it can never work, and has no backward compatibility issue? In a situation such as: ```d struct Parent { void method1() { method2(); } void method2() { } } struct Child : Parent { override void method2() { } } ``` My general view is that you have to be a little bit smart about it. There may be reasons you would want something like this, but there is also reasons it might not be desired. I want a way to reinterpret parent methods as if it was in the child if not overriden. At which point ``method1`` would see the child ``method2`` rather than the one in the parent. |
December 04 Re: Struct inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Richard (Rikki) Andrew Cattermole | On Wednesday, 4 December 2024 at 12:43:49 UTC, Richard (Rikki) Andrew Cattermole wrote: > Casting a pointer to the parent, would be ``@system``, so what it does is very much "good luck with that". Which I guess is reasonable, or even have it as undefined behaviour. > In a situation such as: > > ```d > struct Parent { > void method1() { > method2(); > } > > void method2() { > } > } > > struct Child : Parent { > override void method2() { > } > } > ``` > > My general view is that you have to be a little bit smart about it. > > There may be reasons you would want something like this, but there is also reasons it might not be desired. My thinking, and reason for asking, is that two different authors may be involved. One would not wish the fact that someone had Child inherit from Parent cause the behaviour of Parent to change, that way lies madness. Given these are structs, not classes; and the author of Parent would have no expectation of anything other than its own method being called. > I want a way to reinterpret parent methods as if it was in the child if not overriden. At which point ``method1`` would see the child ``method2`` rather than the one in the parent. Isn't that simply what I described as 'madness' above? One would seem to be exposing the Parent method to an unexpected code change, as one can not know what other Parent private state may be updated based upon the call to method2 being changed. That strikes me as causing non-determinism. I could see a use for this sort of thing: ```d struct Parent { void method1() { method3(); } void method2() { } void method3() { } } struct Child : Parent { override void method2() { method1(); } override void method3() { } } ``` Where the Child call from method2() to method1() invokes the Parent version, yet the Parent call from method1() to method3() will only ever invoke the Parent version. That means the behaviour of Parent stays consistent. To allow method3() to be redirected, is classful behaviour, and should not be forced upon an existing library of structs just because of what the child did. If one wants classes, use classes - as that sort of result is expected to be possible there. |
December 05 Re: Struct inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Derek Fawcus | On 05/12/2024 6:22 AM, Derek Fawcus wrote:
> I want a way to reinterpret parent methods as if it was in the child
> if not overriden. At which point |method1| would see the child |
> method2| rather than the one in the parent.
>
> Isn't that simply what I described as 'madness' above?
>
> One would seem to be exposing the Parent method to an unexpected code change, as one can not know what other Parent private state may be updated based upon the call to method2 being changed.
> That strikes me as causing non-determinism.
>
> I could see a use for this sort of thing:
>
> |struct Parent { void method1() { method3(); } void method2() { } void method3() { } } struct Child : Parent { override void method2() { method1(); } override void method3() { } } |
>
> Where the Child call from method2() to method1() invokes the Parent version, yet the Parent call from method1() to method3() will only ever invoke the Parent version.
>
> That means the behaviour of Parent stays consistent. To allow method3() to be redirected, is classful behaviour, and should not be forced upon an existing library of structs just because of what the child did.
>
> If one wants classes, use classes - as that sort of result is expected to be possible there.
You need this for things like serialization.
Since we don't have runtime reflection it has to be done at CT, and if you have to do anything to offer it as a user, its just a NOPE compared to other languages.
Its needed for both structs and classes.
|
December 05 Re: Struct inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Richard (Rikki) Andrew Cattermole | On Wednesday, 4 December 2024 at 21:49:02 UTC, Richard (Rikki) Andrew Cattermole wrote: > > You need this for things like serialization. I'm not seeing that... > Since we don't have runtime reflection it has to be done at CT, and if you have to do anything to offer it as a user, its just a NOPE compared to other languages. Given that Go and Java use a form of UDA for assisting in serialisation, I don't see that the equivalent for D would be unacceptable. One either writes complex schemes for per structure callbacks, or one uses simple annotation with UDA. I know which I prefer, since it involves writing less code. |
December 06 Re: Struct inheritance | ||||
---|---|---|---|---|
| ||||
Posted in reply to Derek Fawcus | On 06/12/2024 8:12 AM, Derek Fawcus wrote:
> Since we don't have runtime reflection it has to be done at CT, and
> if you have to do anything to offer it as a user, its just a NOPE
> compared to other languages.
>
> Given that Go and Java use a form of UDA for assisting in serialisation, I don't see that the equivalent for D would be unacceptable.
>
> One either writes complex schemes for per structure callbacks, or one uses simple annotation with UDA. I know which I prefer, since it involves writing less code.
I've written many serialization like libraries in D using UDA's they are amazing.
But you still need access to the child most type to do the introspection upon, and that in the case of classes means the user writing code for every child class.
|
Copyright © 1999-2021 by the D Language Foundation