www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Can we just have struct inheritence already?

reply Manu <turkeyman gmail.com> writes:
I am really really tired of this pattern:

struct DerivedStruct
{
    static if (BaseStruct.tupleof.length > 0)
        BaseStruct base;
    else
        ref inout(BaseStruct) base() inout { return
*cast(inout(BaseStruct)*)&this; }
    alias base this;

    // derived members
    //...
}

Imagine if we could just write:

struct DerivedStruct : BaseStruct
{
    // derived members
    //...
}

Just imagine!
Jun 09 2019
next sibling parent reply KnightMare <black80 bk.ru> writes:
On Sunday, 9 June 2019 at 08:05:34 UTC, Manu wrote:

 struct DerivedStruct : BaseStruct {
     // derived members
 }
 Just imagine!
+1 what is the problem not doing this? copying is not to difficult: blit and call postblit for all members if they have it. what the problem i forgot? you want to make better C, so make code familiar for C-users. why need this problems out of the blue?
Jun 09 2019
next sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Sunday, 9 June 2019 at 08:19:46 UTC, KnightMare wrote:

 what is the problem not doing this?
 copying is not to difficult: blit and call postblit for all 
 members if they have it.
 what the problem i forgot?
Object slicing. It's a problem with inherited types when passed by value. https://stackoverflow.com/questions/274626/what-is-object-slicing#274636
Jun 09 2019
next sibling parent KnightMare <black80 bk.ru> writes:
On Sunday, 9 June 2019 at 08:39:09 UTC, Mike Parker wrote:
 Object slicing. It's a problem with inherited types when passed 
 by value.

 https://stackoverflow.com/questions/274626/what-is-object-slicing#274636
in C++ this problem solved easy: cut data to base struct and u cant call methods of derived struct. nobody asks virtuals in struct
Jun 09 2019
prev sibling parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Sunday, 9 June 2019 at 08:39:09 UTC, Mike Parker wrote:
 On Sunday, 9 June 2019 at 08:19:46 UTC, KnightMare wrote:

 what is the problem not doing this?
 copying is not to difficult: blit and call postblit for all 
 members if they have it.
 what the problem i forgot?
Object slicing. It's a problem with inherited types when passed by value. https://stackoverflow.com/questions/274626/what-is-object-slicing#274636
Use private inheritance of base class.
Jun 09 2019
parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Sunday, 9 June 2019 at 11:31:46 UTC, Ola Fosheim Grøstad wrote:
 Use private inheritance of base class.
Or add a feature that make subclass references const so that you cannot assign to just the base class, unless the subclass explicitly allows it. Many possibilities for preventing assignment to base class only by using static typing. Of course, it has to be thought through. A third option is to provide "tagged" struct where a classifier function can determine the object type and pick the right "virtual" assignment operator (using a switch statement internally). A fourth option is to prevent sub-structs from overriding methods in the base struct and forbid assignment to be sub-struct dependent. A fifth option would be to have abstract base structs that cannot be instantiated, and make it so it has no default methods/operators. I assume this would allow multiple inheritance of abstract base structs. You could create new abstract base structs that inherits from other abstract base structs that are non-referencable outside the abstract base struct (so that you can resolve name collisions).
Jun 09 2019
prev sibling parent KnightMare <black80 bk.ru> writes:
On Sunday, 9 June 2019 at 08:19:46 UTC, KnightMare wrote:
 On Sunday, 9 June 2019 at 08:05:34 UTC, Manu wrote:

 struct DerivedStruct : BaseStruct {
     // derived members
 }
 Just imagine!
+1 you want to make better C, so make code familiar for C-users.
oops! C hasn't struct inheritance. my fault and excuse. but it simple comfortable feature.
Jun 09 2019
prev sibling next sibling parent reply Andre Pany <andre s-e-a-p.de> writes:
On Sunday, 9 June 2019 at 08:05:34 UTC, Manu wrote:
 I am really really tired of this pattern:

 struct DerivedStruct
 {
     static if (BaseStruct.tupleof.length > 0)
         BaseStruct base;
     else
         ref inout(BaseStruct) base() inout { return
 *cast(inout(BaseStruct)*)&this; }
     alias base this;

     // derived members
     //...
 }

 Imagine if we could just write:

 struct DerivedStruct : BaseStruct
 {
     // derived members
     //...
 }

 Just imagine!
Users would then also expect they can use the derived structures in places where there ancestors are expected, similar like with classes;) And also they can overload the functions of this structures. Then we need to add a monitor field to all structures to enabled it. It seems like a small change, but opens a huge area of problems. Kind regards Andre
Jun 09 2019
parent KnightMare <black80 bk.ru> writes:
On Sunday, 9 June 2019 at 08:31:40 UTC, Andre Pany wrote:
 Users would then also expect they can use the derived 
 structures in places where there ancestors are expected, 
 similar like with classes;)
 And also they can overload the functions of this structures.
 Then we need to add a monitor field to all structures to 
 enabled it.
not really. people will try to use keywords "virtual" or "override" and compiler will say/stop it. enough to get burned once.
Jun 09 2019
prev sibling next sibling parent Exil <Exil gmall.com> writes:
On Sunday, 9 June 2019 at 08:05:34 UTC, Manu wrote:
 I am really really tired of this pattern:

 struct DerivedStruct
 {
     static if (BaseStruct.tupleof.length > 0)
         BaseStruct base;
     else
         ref inout(BaseStruct) base() inout { return
 *cast(inout(BaseStruct)*)&this; }
     alias base this;

     // derived members
     //...
 }

 Imagine if we could just write:

 struct DerivedStruct : BaseStruct
 {
     // derived members
     //...
 }

 Just imagine!
You can just put the boiler plate code in a mixin template. mixin template BaseAs(T) { static if(T.tupleof.length > 0) T base; else ref inout(T) base() inout { return *cast(inout(T)*)&this; } alias base this; } struct DerivedStruct { mixin BaseAs!BaseStruct; }
Jun 09 2019
prev sibling next sibling parent Exil <Exil gmall.com> writes:
On Sunday, 9 June 2019 at 08:05:34 UTC, Manu wrote:
 I am really really tired of this pattern:

 struct DerivedStruct
 {
     static if (BaseStruct.tupleof.length > 0)
         BaseStruct base;
     else
         ref inout(BaseStruct) base() inout { return
 *cast(inout(BaseStruct)*)&this; }
     alias base this;

     // derived members
     //...
 }

 Imagine if we could just write:

 struct DerivedStruct : BaseStruct
 {
     // derived members
     //...
 }

 Just imagine!
You can just put the boiler plate code in a mixin template. mixin template BaseAs(T) { static if(T.tupleof.length > 0) T base; else ref inout(T) base() inout { return *cast(inout(T)*)&this; } alias base this; } struct DerivedStruct { mixin BaseAs!BaseStruct; }
Jun 09 2019
prev sibling next sibling parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Sunday, 9 June 2019 at 08:05:34 UTC, Manu wrote:
 I am really really tired of this pattern:

 struct DerivedStruct
 {
     static if (BaseStruct.tupleof.length > 0)
         BaseStruct base;
     else
         ref inout(BaseStruct) base() inout { return
 *cast(inout(BaseStruct)*)&this; }
     alias base this;

     // derived members
     //...
 }

 Imagine if we could just write:

 struct DerivedStruct : BaseStruct
 {
     // derived members
     //...
 }

 Just imagine!
What is wrong with alias this?
Jun 09 2019
parent reply KnightMare <black80 bk.ru> writes:
On Sunday, 9 June 2019 at 16:45:18 UTC, 12345swordy wrote:

 What is wrong with alias this?
in my mind "alias this" is a crutch that allow to move on, that tries to replace "has-a" with "is-a" and at the same time introduces an implicit conversion operator. last one can be alone: decimal to double with no inheritance. in your my its cool thing and "let me sign your cast". well, just two different points of view at one thing. BUT in case where I want "is-a" from one type and implicit cast to another my point became more correct. BUT I can't think of an example right now, maybe no such example at all. BUT I still has more control in my hands with is-a/has-a and implicit opCast(if it will be added).
Jun 09 2019
next sibling parent reply KnightMare <black80 bk.ru> writes:
On Sunday, 9 June 2019 at 17:09:44 UTC, KnightMare wrote:
 in your my its cool thing and "let me sign your cast".
*your mind this is cool thing* PS need to allow edit post for 5min at at least
Jun 09 2019
parent reply SashaGreat <sasha gmail.com> writes:
On Sunday, 9 June 2019 at 17:11:37 UTC, KnightMare wrote:
 On Sunday, 9 June 2019 at 17:09:44 UTC, KnightMare wrote:
 in your my its cool thing and "let me sign your cast".
*your mind this is cool thing* PS need to allow edit post for 5min at at least
Or you can: 1) Revise it better before posting. 2) If you use e-mail instead of WEB Front-End, some e-mail services and applications (Like Thunderbird) allows you to hold it for some time before sending.
Jun 09 2019
parent reply KnightMare <black80 bk.ru> writes:
On Sunday, 9 June 2019 at 17:26:40 UTC, SashaGreat wrote:

 PS need to allow edit post for 5min at at least
Or you can: 1) Revise it better before posting. 2) If you use e-mail instead of WEB Front-End, some e-mail services and applications (Like Thunderbird) allows you to hold it for some time before sending.
people make mistakes that can't be changed. 1) please dont write AI for people control system :) 2) too complicated. my suggestion (allow to edit post in next 5mins) is more humane.
Jun 09 2019
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, June 9, 2019 11:35:28 AM MDT KnightMare via Digitalmars-d wrote:
 On Sunday, 9 June 2019 at 17:26:40 UTC, SashaGreat wrote:
 PS need to allow edit post for 5min at at least
Or you can: 1) Revise it better before posting. 2) If you use e-mail instead of WEB Front-End, some e-mail services and applications (Like Thunderbird) allows you to hold it for some time before sending.
people make mistakes that can't be changed. 1) please dont write AI for people control system :) 2) too complicated. my suggestion (allow to edit post in next 5mins) is more humane.
This is a newsgroup which can also be accessed via a web interface and via a mailing list. Once a message has been sent, it's been sent. It immediately goes out to everyone over the various interfaces, and it wouldn't make any more sense to try to edit your message after sending it than it would to try to edit an e-mail after you sent it to hundreds of people. - Jonathan M Davis
Jun 09 2019
parent reply Exil <Exil gmall.com> writes:
On Sunday, 9 June 2019 at 17:57:05 UTC, Jonathan M Davis wrote:
 On Sunday, June 9, 2019 11:35:28 AM MDT KnightMare via 
 Digitalmars-d wrote:
 On Sunday, 9 June 2019 at 17:26:40 UTC, SashaGreat wrote:
 PS need to allow edit post for 5min at at least
Or you can: 1) Revise it better before posting. 2) If you use e-mail instead of WEB Front-End, some e-mail services and applications (Like Thunderbird) allows you to hold it for some time before sending.
people make mistakes that can't be changed. 1) please dont write AI for people control system :) 2) too complicated. my suggestion (allow to edit post in next 5mins) is more humane.
This is a newsgroup which can also be accessed via a web interface and via a mailing list. Once a message has been sent, it's been sent. It immediately goes out to everyone over the various interfaces, and it wouldn't make any more sense to try to edit your message after sending it than it would to try to edit an e-mail after you sent it to hundreds of people. - Jonathan M Davis
Github also sends messages via email, you can even make replies by email. You can still edit them afterwards though.
Jun 09 2019
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, June 9, 2019 12:19:34 PM MDT Exil via Digitalmars-d wrote:
 On Sunday, 9 June 2019 at 17:57:05 UTC, Jonathan M Davis wrote:
 On Sunday, June 9, 2019 11:35:28 AM MDT KnightMare via

 Digitalmars-d wrote:
 On Sunday, 9 June 2019 at 17:26:40 UTC, SashaGreat wrote:
 PS need to allow edit post for 5min at at least
Or you can: 1) Revise it better before posting. 2) If you use e-mail instead of WEB Front-End, some e-mail services and applications (Like Thunderbird) allows you to hold it for some time before sending.
people make mistakes that can't be changed. 1) please dont write AI for people control system :) 2) too complicated. my suggestion (allow to edit post in next 5mins) is more humane.
This is a newsgroup which can also be accessed via a web interface and via a mailing list. Once a message has been sent, it's been sent. It immediately goes out to everyone over the various interfaces, and it wouldn't make any more sense to try to edit your message after sending it than it would to try to edit an e-mail after you sent it to hundreds of people. - Jonathan M Davis
Github also sends messages via email, you can even make replies by email. You can still edit them afterwards though.
But no one gets those updates without looking at github, and even if they did, it would have to be via a new e-mail, cluttering things up. It's not possible for anyone to edit their message on github and then have the e-mails that everyone received update. The e-mails from github serve as notifications of what's on the website, not as the primary means of communication. And for D, the web interface is _not_ what controls the data. It's a client just like my e-mail client is and just like Walter's newsgroup client is. So, all it can edit is its local cache, which would make it out-of-sync with everyone else. Or it could send out a second message - which is exactly what you have to do right now. And because that's what you have to do, it doesn't give the false impression that you can edit previously sent messages. - Jonathan M Davis
Jun 09 2019
parent reply Exil <Exil gmall.com> writes:
On Sunday, 9 June 2019 at 18:43:01 UTC, Jonathan M Davis wrote:
 On Sunday, June 9, 2019 12:19:34 PM MDT Exil via Digitalmars-d 
 wrote:
 On Sunday, 9 June 2019 at 17:57:05 UTC, Jonathan M Davis wrote:
 On Sunday, June 9, 2019 11:35:28 AM MDT KnightMare via

 Digitalmars-d wrote:
 On Sunday, 9 June 2019 at 17:26:40 UTC, SashaGreat wrote:
 PS need to allow edit post for 5min at at least
Or you can: 1) Revise it better before posting. 2) If you use e-mail instead of WEB Front-End, some e-mail services and applications (Like Thunderbird) allows you to hold it for some time before sending.
people make mistakes that can't be changed. 1) please dont write AI for people control system :) 2) too complicated. my suggestion (allow to edit post in next 5mins) is more humane.
This is a newsgroup which can also be accessed via a web interface and via a mailing list. Once a message has been sent, it's been sent. It immediately goes out to everyone over the various interfaces, and it wouldn't make any more sense to try to edit your message after sending it than it would to try to edit an e-mail after you sent it to hundreds of people. - Jonathan M Davis
Github also sends messages via email, you can even make replies by email. You can still edit them afterwards though.
But no one gets those updates without looking at github, and even if they did, it would have to be via a new e-mail, cluttering things up. It's not possible for anyone to edit their message on github and then have the e-mails that everyone received update. The e-mails from github serve as notifications of what's on the website, not as the primary means of communication.
That's because emails aren't suited for this type of communication. If you want to use emails, then yes you would then receive a second email. But if someone needs to make a correction anyways, they will need to send a second email as well. This not only clutters up the email but also the website as well. Which is what we have now.
 And for D, the web interface is _not_ what controls the data. 
 It's a client just like my e-mail client is and just like 
 Walter's newsgroup client is. So, all it can edit is its local 
 cache, which would make it out-of-sync with everyone else. Or 
 it could send out a second message - which is exactly what you 
 have to do right now. And because that's what you have to do, 
 it doesn't give the false impression that you can edit 
 previously sent messages.

 - Jonathan M Davis
And? This is set in stone? There's no way to implement something better?
Jun 09 2019
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 6/9/19 2:52 PM, Exil wrote:
 And? This is set in stone? There's no way to implement something better?
Have at it!
Jun 09 2019
prev sibling next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, June 9, 2019 12:52:20 PM MDT Exil via Digitalmars-d wrote:
 On Sunday, 9 June 2019 at 18:43:01 UTC, Jonathan M Davis wrote:
 On Sunday, June 9, 2019 12:19:34 PM MDT Exil via Digitalmars-d

 wrote:
 On Sunday, 9 June 2019 at 17:57:05 UTC, Jonathan M Davis wrote:
 On Sunday, June 9, 2019 11:35:28 AM MDT KnightMare via

 Digitalmars-d wrote:
 On Sunday, 9 June 2019 at 17:26:40 UTC, SashaGreat wrote:
 PS need to allow edit post for 5min at at least
Or you can: 1) Revise it better before posting. 2) If you use e-mail instead of WEB Front-End, some e-mail services and applications (Like Thunderbird) allows you to hold it for some time before sending.
people make mistakes that can't be changed. 1) please dont write AI for people control system :) 2) too complicated. my suggestion (allow to edit post in next 5mins) is more humane.
This is a newsgroup which can also be accessed via a web interface and via a mailing list. Once a message has been sent, it's been sent. It immediately goes out to everyone over the various interfaces, and it wouldn't make any more sense to try to edit your message after sending it than it would to try to edit an e-mail after you sent it to hundreds of people. - Jonathan M Davis
Github also sends messages via email, you can even make replies by email. You can still edit them afterwards though.
But no one gets those updates without looking at github, and even if they did, it would have to be via a new e-mail, cluttering things up. It's not possible for anyone to edit their message on github and then have the e-mails that everyone received update. The e-mails from github serve as notifications of what's on the website, not as the primary means of communication.
That's because emails aren't suited for this type of communication. If you want to use emails, then yes you would then receive a second email. But if someone needs to make a correction anyways, they will need to send a second email as well. This not only clutters up the email but also the website as well. Which is what we have now.
Mailing lists have worked quite well for this type of communication for decades. So have newsgroups. And if you just reread what you're posting before you post it, you can avoid needing to edit it or send a response to your message to correct it. As it is, only a small percentage of posts in this newsgroup end up with the poster replying to correct their post.
 And for D, the web interface is _not_ what controls the data.
 It's a client just like my e-mail client is and just like
 Walter's newsgroup client is. So, all it can edit is its local
 cache, which would make it out-of-sync with everyone else. Or
 it could send out a second message - which is exactly what you
 have to do right now. And because that's what you have to do,
 it doesn't give the false impression that you can edit
 previously sent messages.

 - Jonathan M Davis
And? This is set in stone? There's no way to implement something better?
Many of us use the mailing list or newsgroup interfaces and have no interest in using the web interface. If the web interface can be improved in ways that work well with the newsgroup and the mailing list interfaces, then fine. Have at it. But editing messages really doesn't work well with that. And if you want to move away from the backend being a newsgroup with a mailing list as one of the interfaces, you're going to find a fair bit of opposition to that. - Jonathan M Davis
Jun 09 2019
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09.06.19 20:52, Exil wrote:
 
 And? This is set in stone? There's no way to implement something better?
Edit functionality is not "something better". It's perfectly fine that you can't retroactively change what you said in the past.
Jun 09 2019
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 6/9/19 1:09 PM, KnightMare wrote:
 On Sunday, 9 June 2019 at 16:45:18 UTC, 12345swordy wrote:
 
 What is wrong with alias this?
in my mind "alias this" is a crutch that allow to move on, that tries to replace "has-a" with "is-a" and at the same time introduces an implicit conversion operator.
Sadly it was meant as a generalized "is-a". Implementation botched that, I'm afraid, beyond repair. We'd be better off deprecating it and allowing some simple form of subtyping for structs.
Jun 09 2019
parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Sunday, 9 June 2019 at 19:47:26 UTC, Andrei Alexandrescu wrote:

 Sadly it was meant as a generalized "is-a". Implementation 
 botched that, I'm afraid, beyond repair. We'd be better off 
 deprecating it and allowing some simple form of subtyping for 
 structs.
I like the `alias this` feature quite a lot, but it does have a kludgy feeling to it when you get some experience with it. For example, overload rules (or lack thereof). I don't think of it so much as an "is-a" relationship, but more as a kind of mixin (for lack of a better word) where you mix the public interface of an aggregate's field with the public interface of the aggregate itself. Though, now that I think of it, I wonder if that can be achieved with metaprogramming instead. In that case, I'd probably prefer the metaprogramming technique. I'm curious, though, so I welcome more elaboration on the shortcomings of the implementation. Mike
Jun 09 2019
parent Mike Franklin <slavo5150 yahoo.com> writes:
On Monday, 10 June 2019 at 02:12:55 UTC, Mike Franklin wrote:

 For example, overload rules (or lack thereof).
Sorry, I mean resolution rules.
Jun 09 2019
prev sibling next sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 6/9/19 4:05 AM, Manu wrote:
 I am really really tired of this pattern:
 
 struct DerivedStruct
 {
      static if (BaseStruct.tupleof.length > 0)
          BaseStruct base;
      else
          ref inout(BaseStruct) base() inout { return
 *cast(inout(BaseStruct)*)&this; }
      alias base this;
 
      // derived members
      //...
 }
 
 Imagine if we could just write:
 
 struct DerivedStruct : BaseStruct
 {
      // derived members
      //...
 }
 
 Just imagine!
 
Not necessarily objecting here, but does this pattern really come up all that often? I'm not even entirely sure what's going on in the "static else" there. Is it just saying: "If the BaseStruct is all methods and no data, then don't bother letting `BaseStruct base` add extra obviously-never-used bytes in DerivedStruct?" If so, it sounds like maybe that's an optimization the language/compiler could/should be doing (semi-?)automatically?
Jun 09 2019
parent reply Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Sunday, 9 June 2019 at 19:04:50 UTC, Nick Sabalausky 
(Abscissa) wrote:
 I'm not even entirely sure what's going on in the "static else" 
 there. Is it just saying:

 "If the BaseStruct is all methods and no data, then don't 
 bother letting `BaseStruct base` add extra obviously-never-used 
 bytes in DerivedStruct?"

 If so, it sounds like maybe that's an optimization the 
 language/compiler could/should be doing (semi-?)automatically?
Structs are always at least 1 byte in size, even when they contain no fields. This causes unnecessary bloat for reasons that I never understood.
Jun 09 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/9/2019 12:40 PM, Vladimir Panteleev wrote:
 Structs are always at least 1 byte in size, even when they contain no fields. 
 This causes unnecessary bloat for reasons that I never understood.
The reason is so that each struct instance is at a unique address. There are some rules in C++ where sometimes a struct with no fields occupies 1 byte, sometimes 0 bytes. I don't recall what they are at the moment.
Jun 09 2019
next sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Monday, 10 June 2019 at 03:08:26 UTC, Walter Bright wrote:
 On 6/9/2019 12:40 PM, Vladimir Panteleev wrote:
 Structs are always at least 1 byte in size, even when they 
 contain no fields. This causes unnecessary bloat for reasons 
 that I never understood.
The reason is so that each struct instance is at a unique address. There are some rules in C++ where sometimes a struct with no fields occupies 1 byte, sometimes 0 bytes. I don't recall what they are at the moment.
There is a (proposal?) for a no unique address attribute to disappear the address of structs the take up no room.
Jun 09 2019
next sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Monday, 10 June 2019 at 04:03:38 UTC, Nicholas Wilson wrote:
 There is a (proposal?) for a no unique address attribute to 
 disappear the address of structs the take up no room.
Found it: https://en.cppreference.com/w/cpp/language/attributes/no_unique_address Looks like there are a bunch of weird edge cases with it though: struct Empty {}; struct Z { char c; [[no_unique_address]] Empty e1, e2; }; // e1 and e2 cannot share the same address because they have the // same type, even though they are marked with [[no_unique_address]]. // However, either may share address with c. static_assert(sizeof(Z) >= 2);
Jun 09 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/9/2019 10:13 PM, Nicholas Wilson wrote:
 On Monday, 10 June 2019 at 04:03:38 UTC, Nicholas Wilson wrote:
 There is a (proposal?) for a no unique address attribute to disappear the 
 address of structs the take up no room.
Found it: https://en.cppreference.com/w/cpp/language/attributes/no_unique_address Looks like there are a bunch of weird edge cases with it though: struct Empty {}; struct Z {     char c;     [[no_unique_address]] Empty e1, e2; };     // e1 and e2 cannot share the same address because they have the     // same type, even though they are marked with [[no_unique_address]].     // However, either may share address with c.     static_assert(sizeof(Z) >= 2);
I didn't know about that attribute. Thanks for digging it up. Sheesh.
Jun 10 2019
parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Monday, 10 June 2019 at 09:35:25 UTC, Walter Bright wrote:
 I didn't know about that attribute. Thanks for digging it up.
Np.
 Sheesh.
Touché.
Jun 10 2019
prev sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 6/10/19 12:03 AM, Nicholas Wilson wrote:
 On Monday, 10 June 2019 at 03:08:26 UTC, Walter Bright wrote:
 On 6/9/2019 12:40 PM, Vladimir Panteleev wrote:
 Structs are always at least 1 byte in size, even when they contain no 
 fields. This causes unnecessary bloat for reasons that I never 
 understood.
The reason is so that each struct instance is at a unique address. There are some rules in C++ where sometimes a struct with no fields occupies 1 byte, sometimes 0 bytes. I don't recall what they are at the moment.
There is a (proposal?) for a no unique address attribute to disappear the address of structs the take up no room.
Would it really be a problem for the addresses of zero-byte structs to just simply be null?
Jun 10 2019
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Jun 10, 2019 at 06:35:16PM -0400, Nick Sabalausky (Abscissa) via
Digitalmars-d wrote:
 On 6/10/19 12:03 AM, Nicholas Wilson wrote:
 On Monday, 10 June 2019 at 03:08:26 UTC, Walter Bright wrote:
 On 6/9/2019 12:40 PM, Vladimir Panteleev wrote:
 Structs are always at least 1 byte in size, even when they
 contain no fields. This causes unnecessary bloat for reasons
 that I never understood.
The reason is so that each struct instance is at a unique address. There are some rules in C++ where sometimes a struct with no fields occupies 1 byte, sometimes 0 bytes. I don't recall what they are at the moment.
There is a (proposal?) for a no unique address attribute to disappear the address of structs the take up no room.
Would it really be a problem for the addresses of zero-byte structs to just simply be null?
Probably, if people expect: struct S {} S s; assert(&s !is null); to work. TBH, though, expecting different instances of a 0-sized struct to have distinct addresses is kind of a strange obscure case, and I'm not convinced it's worth the resulting slew of messy special cases just to cater to it. But that ship has long since sailed, and I'm not sure it's worth fighting over anymore. T -- Chance favours the prepared mind. -- Louis Pasteur
Jun 10 2019
parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 6/10/19 7:00 PM, H. S. Teoh wrote:
 On Mon, Jun 10, 2019 at 06:35:16PM -0400, Nick Sabalausky (Abscissa) via
Digitalmars-d wrote:
 Would it really be a problem for the addresses of zero-byte structs to
 just simply be null?
Probably, if people expect: struct S {} S s; assert(&s !is null); to work.
Ehh, maybe, but since when does anyone ever check a D struct's address for being null? Even if you DID have a struct who's address was null (due to being zero size), then unlike for classes, using the struct's methods or static members would still be no problem since it wouldn't involve dereferencing the null (Instead, for the static members it's irrelevent entirely, and for the rest: it would just simply pass null as the hidden argument to member functions which - outside of system pointer manipulation - would still have no way to actually dereference the null, because there's no non-static data members to even refer to, let alone access.) Or, heck, even just have it share an address with another data member. That or the null thing, I see no realistic problem either way. Or if you really want to be needlessly clumsy about it, have an attribute or pragma or something that says "If this field is a zero-byte struct, then for the love of god make it zero byte, I don't even care how you do it." I really don't like the whole "ships have sailed" theme in D though. It just gives a straight up middle finger to "The Last Thing D Needs" and to the whole reason for D's creation in the first place - to fix stupid legacy mistakes. And yea, sure, of course we have bigger fish to fry. But that should never be a reason to say "The mistake is realistically fixable, but NO, never gonna anyway!" instead of "Right, that was a mistake and it's fixable. We should fix it sooner or later BUT it's not a high priority right now."
Jun 11 2019
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jun 12, 2019 at 12:31:24AM -0400, Nick Sabalausky (Abscissa) via
Digitalmars-d wrote:
 On 6/10/19 7:00 PM, H. S. Teoh wrote:
 On Mon, Jun 10, 2019 at 06:35:16PM -0400, Nick Sabalausky (Abscissa) via
Digitalmars-d wrote:
 
 Would it really be a problem for the addresses of zero-byte
 structs to just simply be null?
Probably, if people expect: struct S {} S s; assert(&s !is null); to work.
Ehh, maybe, but since when does anyone ever check a D struct's address for being null?
I wouldn't say it occurs to structs specifically, but in generic code, it's conceivable you'd want to receive pointers and check whether they're actually pointing to something or if there's no value (e.g., using null as a kind of absence-of-data indicator). There might be unexpected interaction with code that uses null for indicating, e.g., whether a container slot exists, which becomes problematic if null is also the address of an instance of a 0-sized type (thereby conflating "non-existent slot" with "slot exists but data is 0 bytes). This is kinda contrived, though, and honestly I would vote for not supporting this sort of usage. But it's the kind of thing certain people might get up in arms about, if this behaviour were changed.
 Or, heck, even just have it share an address with another data member.
 That or the null thing, I see no realistic problem either way. Or if
 you really want to be needlessly clumsy about it, have an attribute or
 pragma or something that says "If this field is a zero-byte struct,
 then for the love of god make it zero byte, I don't even care how you
 do it."
Maybe just alias it to void[0], like somebody proposed?
 I really don't like the whole "ships have sailed" theme in D though.
 It just gives a straight up middle finger to "The Last Thing D Needs"
 and to the whole reason for D's creation in the first place - to fix
 stupid legacy mistakes.
Personally, I agree that we shouldn't shy away from breaking changes if it improves the language for the long term. After all, that's what the deprecation process is for. But I'm not holding my breath for D leadership to buy into this sort of breaking change.
 And yea, sure, of course we have bigger fish to fry. But that should
 never be a reason to say "The mistake is realistically fixable, but
 NO, never gonna anyway!" instead of "Right, that was a mistake and
 it's fixable. We should fix it sooner or later BUT it's not a high
 priority right now."
In fact, I agree. But I'm also not holding my breath for D leadership to change this attitude anytime soon. Cf. the bool fiasco, the char -> int implicit conversion (C promotion rules) fiasco, autodecoding (which is by now universally acknowledged as a mistake, but nobody has yet dared to take the first step to actually kill it), etc.. At some point, I seriously want to entertain the idea of D3. It used to be immediately shot down as not being likely, but with Andrei's recent change of stance about std.v2, there's a faint glimmer of hope that perhaps one day D3 could become reality. T -- Gone Chopin. Bach in a minuet.
Jun 11 2019
parent Exil <Exil gmall.com> writes:
On Wednesday, 12 June 2019 at 06:00:12 UTC, H. S. Teoh wrote:
 This is kinda contrived, though, and honestly I would vote for 
 not supporting this sort of usage.  But it's the kind of thing 
 certain people might get up in arms about, if this behaviour 
 were changed.


 Or, heck, even just have it share an address with another data 
 member. That or the null thing, I see no realistic problem 
 either way. Or if you really want to be needlessly clumsy 
 about it, have an attribute or pragma or something that says 
 "If this field is a zero-byte struct, then for the love of god 
 make it zero byte, I don't even care how you do it."
Maybe just alias it to void[0], like somebody proposed?
Since you'd still have to "alias this" the alias, it would probably cause more problem as it would now act as an array in some regard.
 And yea, sure, of course we have bigger fish to fry. But that 
 should never be a reason to say "The mistake is realistically 
 fixable, but NO, never gonna anyway!" instead of "Right, that 
 was a mistake and it's fixable. We should fix it sooner or 
 later BUT it's not a high priority right now."
In fact, I agree. But I'm also not holding my breath for D leadership to change this attitude anytime soon. Cf. the bool fiasco, the char -> int implicit conversion (C promotion rules) fiasco, autodecoding (which is by now universally acknowledged as a mistake, but nobody has yet dared to take the first step to actually kill it), etc.. At some point, I seriously want to entertain the idea of D3. It used to be immediately shot down as not being likely, but with Andrei's recent change of stance about std.v2, there's a faint glimmer of hope that perhaps one day D3 could become reality. T
Don't think it will. If D3 happens, then that means there will be D2 code that can't work with D3. Creating a std.v2 means that old code can just use the old version of the std and still work without having to rewrite their code. They can also use new D features while still using the old std library. One is a lot easier to deal with than the other.
Jun 12 2019
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, June 10, 2019 5:00:40 PM MDT H. S. Teoh via Digitalmars-d wrote:
 On Mon, Jun 10, 2019 at 06:35:16PM -0400, Nick Sabalausky (Abscissa) via 
Digitalmars-d wrote:
 On 6/10/19 12:03 AM, Nicholas Wilson wrote:
 On Monday, 10 June 2019 at 03:08:26 UTC, Walter Bright wrote:
 On 6/9/2019 12:40 PM, Vladimir Panteleev wrote:
 Structs are always at least 1 byte in size, even when they
 contain no fields. This causes unnecessary bloat for reasons
 that I never understood.
The reason is so that each struct instance is at a unique address. There are some rules in C++ where sometimes a struct with no fields occupies 1 byte, sometimes 0 bytes. I don't recall what they are at the moment.
There is a (proposal?) for a no unique address attribute to disappear the address of structs the take up no room.
Would it really be a problem for the addresses of zero-byte structs to just simply be null?
Probably, if people expect: struct S {} S s; assert(&s !is null); to work. TBH, though, expecting different instances of a 0-sized struct to have distinct addresses is kind of a strange obscure case, and I'm not convinced it's worth the resulting slew of messy special cases just to cater to it. But that ship has long since sailed, and I'm not sure it's worth fighting over anymore.
There's also assert(&s); which calls the struct's invariant, though I doubt that many people know that, and unless someone is trying to do that in generic code or has an invariant which checks something other than the member variables' state, it wouldn't really matter if you couldn't do it with a struct with no member variables. - Jonathan M Davis
Jun 10 2019
prev sibling next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Monday, 10 June 2019 at 03:08:26 UTC, Walter Bright wrote:
 There are some rules in C++ where sometimes a struct with no 
 fields occupies 1 byte, sometimes 0 bytes. I don't recall what 
 they are at the moment.
Yes, empty base class optimization: https://en.cppreference.com/w/cpp/language/ebo
Jun 10 2019
prev sibling parent reply Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Monday, 10 June 2019 at 03:08:26 UTC, Walter Bright wrote:
 On 6/9/2019 12:40 PM, Vladimir Panteleev wrote:
 Structs are always at least 1 byte in size, even when they 
 contain no fields. This causes unnecessary bloat for reasons 
 that I never understood.
The reason is so that each struct instance is at a unique address.
What advantages does this provide? Surely it can't be more important than being able to place behavior/policy in structs with no overhead?
Jun 10 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/10/2019 3:49 AM, Vladimir Panteleev wrote:
 On Monday, 10 June 2019 at 03:08:26 UTC, Walter Bright wrote:
 The reason is so that each struct instance is at a unique address.
What advantages does this provide? Surely it can't be more important than being able to place behavior/policy in structs with no overhead?
I don't remember all the rationale (30 years ago!), but a couple reasons are: 1. How would an array of 0 size elements work, when the pointer past the end is the same as the pointer to the start? 2. How would you determine the dimension of an array of 0 size elements? sizeof(Array)/sizeof(Array[0]) ? Oops!
Jun 10 2019
next sibling parent reply Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Tuesday, 11 June 2019 at 06:29:58 UTC, Walter Bright wrote:
 1. How would an array of 0 size elements work, when the pointer 
 past the end is the same as the pointer to the start?

 2. How would you determine the dimension of an array of 0 size 
 elements?

     sizeof(Array)/sizeof(Array[0])

 ? Oops!
But... 0-length arrays in D are already 0 bytes in size. struct S { int a; ubyte[0] b; int c; } pragma(msg, S.tupleof.offsetof); // tuple(0LU, 4LU, 4LU) pragma(msg, S.b.sizeof); // 0LU We actually take advantage of this by declaring hashsets as void[0][Key]. The same applies to two-dimensional 0-length arrays (T[0][0]), which would make them an array with 0-size elements. I don't think that's been a problem.
Jun 11 2019
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
On 11/06/2019 10:02 PM, Vladimir Panteleev wrote:
 We actually take advantage of this by declaring hashsets as void[0][Key].
Oh that's a neat trick, I just use a bool!
Jun 11 2019
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/11/2019 3:02 AM, Vladimir Panteleev wrote:
 [...]
Ok, you got me there. Here's what Stroustrup says: http://www.stroustrup.com/bs_faq2.html#sizeof-empty
Jun 12 2019
parent reply Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Thursday, 13 June 2019 at 05:31:23 UTC, Walter Bright wrote:
 On 6/11/2019 3:02 AM, Vladimir Panteleev wrote:
 [...]
Ok, you got me there. Here's what Stroustrup says: http://www.stroustrup.com/bs_faq2.html#sizeof-empty
From the page: Empty a, b; if (&a == &b) cout << "impossible: report error to compiler supplier"; However, no further justification is provided why that is a problem. Perhaps it's something the C++ spec was written to guarantee, but does not apply to D?
Jun 13 2019
parent reply aliak <something something.com> writes:
On Thursday, 13 June 2019 at 09:44:12 UTC, Vladimir Panteleev 
wrote:
 On Thursday, 13 June 2019 at 05:31:23 UTC, Walter Bright wrote:
 On 6/11/2019 3:02 AM, Vladimir Panteleev wrote:
 [...]
Ok, you got me there. Here's what Stroustrup says: http://www.stroustrup.com/bs_faq2.html#sizeof-empty
From the page: Empty a, b; if (&a == &b) cout << "impossible: report error to compiler supplier"; However, no further justification is provided why that is a problem. Perhaps it's something the C++ spec was written to guarantee, but does not apply to D?
Just for amusement, rust has ZSTs (zero sized types) and Empty types (they distinguish between the two) - https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts And here's a consequence of that: https://github.com/rust-lang/rust/blob/e011fe1c132010e261cc9ea658da73152f1f7fc7/src/liballoc/vec.rs#L2428 Here's a more detailed "beware of these guys" https://gankro.github.io/blah/only-in-rust/#honorable-mention-zero-sized-types-zsts "However, in low level (unsafe) code zero-sized types can be a bit hazardous, for two reasons: If you try to malloc some number of zero-sized types naively, you'll pass an allocation size of 0 to the allocator, which is implementation-defined at best. Also you want to avoid allocating these things anyway! If you try to offset a pointer to a zero-sized type naively, you will get nowhere. This breaks a C-style "two pointers moving towards each other" iterator." Swift and go are also mentioned in the above link. Cheers, - Ali
Jun 13 2019
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Thursday, 13 June 2019 at 10:25:02 UTC, aliak wrote:
 If you try to malloc some number of zero-sized types naively, 
 you'll pass an allocation size of 0 to the allocator, which is 
 implementation-defined at best. Also you want to avoid 
 allocating these things anyway!
On 64 bit architectures you probably could write a custom malloc that will return a unique address outside the memory map for 0-sized allocations. It would have to mark it in a bitmap so that it could be freed. *shrugs* But, I doubt that people actually do this often. Allocating 4-8 bytes probably would be ok, so you could just redefine malloc as malloc(max(allocationsize,8)).
Jun 13 2019
prev sibling parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 13 June 2019 at 10:25:02 UTC, aliak wrote:
 Here's a more detailed "beware of these guys" 
 https://gankro.github.io/blah/only-in-rust/#honorable-mention-zero-sized-types-zsts

 "However, in low level (unsafe) code zero-sized types can be a 
 bit hazardous, for two reasons:

 If you try to malloc some number of zero-sized types naively, 
 you'll pass an allocation size of 0 to the allocator, which is 
 implementation-defined at best. Also you want to avoid 
 allocating these things anyway!
Especially if you use C allocation realloc() as calling it with size 0 is synonymous to free(). Ouch!
Jun 13 2019
parent reply Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Thursday, 13 June 2019 at 12:20:14 UTC, Patrick Schluter wrote:
 If you try to malloc some number of zero-sized types naively, 
 you'll pass an allocation size of 0 to the allocator, which is 
 implementation-defined at best. Also you want to avoid 
 allocating these things anyway!
Especially if you use C allocation realloc() as calling it with size 0 is synonymous to free(). Ouch!
When would that be a problem? Any pointer is a valid pointer for a zero-sized object, as it can never be meaningfully "dereferenced" (never minding only pretending to do that for purposes of metaprogramming and such).
Jun 15 2019
parent Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
On Saturday, 15 June 2019 at 10:19:25 UTC, Vladimir Panteleev 
wrote:
 On Thursday, 13 June 2019 at 12:20:14 UTC, Patrick Schluter 
 wrote:
 Especially if you use C allocation realloc() as calling it 
 with size 0 is synonymous to free(). Ouch!
When would that be a problem? Any pointer is a valid pointer for a zero-sized object, as it can never be meaningfully "dereferenced" (never minding only pretending to do that for purposes of metaprogramming and such).
Oh, I guess it would be an issue if you tried to grow it back to non-zero size.
Jun 15 2019
prev sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 6/11/19 2:29 AM, Walter Bright wrote:
 
 2. How would you determine the dimension of an array of 0 size elements?
By reading .length instead of performing an unnecessary division.
Jun 11 2019
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jun 12, 2019 at 12:44:08AM -0400, Nick Sabalausky (Abscissa) via
Digitalmars-d wrote:
 On 6/11/19 2:29 AM, Walter Bright wrote:
 
 2. How would you determine the dimension of an array of 0 size
 elements?
By reading .length instead of performing an unnecessary division.
+1. I never liked that nonsensical sizeof(array)/sizeof(array[0]) "trick" or idiom. It was a hack around a deficient language way back when. Today we have D, and D has .length. We should be using that instead. Why are we still bending over backwards to support outdated hacks? T -- Freedom of speech: the whole world has no right *not* to hear my spouting off!
Jun 11 2019
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/9/2019 1:05 AM, Manu wrote:
 I am really really tired of this pattern:
Is your real complaint this: --- #include <stdio.h> struct B { }; struct S : B { char c; }; struct T { B b; char c; }; void main(){ printf("%d %d\n", sizeof(struct S), sizeof(struct T)); } --- which prints: 1 2 ?
Jun 09 2019
parent reply Manu <turkeyman gmail.com> writes:
On Sun, Jun 9, 2019 at 9:50 PM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 6/9/2019 1:05 AM, Manu wrote:
 I am really really tired of this pattern:
Is your real complaint this: --- #include <stdio.h> struct B { }; struct S : B { char c; }; struct T { B b; char c; }; void main(){ printf("%d %d\n", sizeof(struct S), sizeof(struct T)); } --- which prints: 1 2 ?
That is indeed the feature that necessitates my 'solution', but `alias this` is generally undesirable in this pattern in its own right, for some of the reasons I listed. `alias this` does not feel satisfying in this context in its own right. Also, if this hack takes the alias this slot, then it can't be used for what it's usually used for (enabling implicit conversion) and sometimes needs conflict.
Jun 09 2019
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/9/2019 10:52 PM, Manu wrote:
 On Sun, Jun 9, 2019 at 9:50 PM Walter Bright via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 6/9/2019 1:05 AM, Manu wrote:
 I am really really tired of this pattern:
Is your real complaint this: --- #include <stdio.h> struct B { }; struct S : B { char c; }; struct T { B b; char c; }; void main(){ printf("%d %d\n", sizeof(struct S), sizeof(struct T)); } --- which prints: 1 2 ?
That is indeed the feature that necessitates my 'solution', but `alias this` is generally undesirable in this pattern in its own right, for some of the reasons I listed.
I don't see where you listed reasons? Other than it just being unsightly?
 `alias this` does not feel satisfying in this context in its own
 right. Also, if this hack takes the alias this slot, then it can't be
 used for what it's usually used for (enabling implicit conversion) and
 sometimes needs conflict.
Are you using 'alias this' to try to do multiple inheritance? Please don't :-(
Jun 10 2019
parent Manu <turkeyman gmail.com> writes:
On Mon, Jun 10, 2019 at 2:40 AM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 6/9/2019 10:52 PM, Manu wrote:
 On Sun, Jun 9, 2019 at 9:50 PM Walter Bright via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 6/9/2019 1:05 AM, Manu wrote:
 I am really really tired of this pattern:
Is your real complaint this: --- #include <stdio.h> struct B { }; struct S : B { char c; }; struct T { B b; char c; }; void main(){ printf("%d %d\n", sizeof(struct S), sizeof(struct T)); } --- which prints: 1 2 ?
That is indeed the feature that necessitates my 'solution', but `alias this` is generally undesirable in this pattern in its own right, for some of the reasons I listed.
I don't see where you listed reasons? Other than it just being unsightly?
Somehow this thread forked from my OP, I listed reasons in my second post there, mostly relating to editor and debug experience. This pattern I illustrate has a tendency to obscure code and intent not just to human readers, but also to the IDE tooling. Syntax highlighting fails, auto-completion fails, debuginfo does not understand the hack and presents incorrectly, etc.
 `alias this` does not feel satisfying in this context in its own
 right. Also, if this hack takes the alias this slot, then it can't be
 used for what it's usually used for (enabling implicit conversion) and
 sometimes needs conflict.
Are you using 'alias this' to try to do multiple inheritance? Please don't :-(
No, I usually use alias this for implicit conversion... which is a pattern I would be overjoyed to abandon if we had some way to mark constructors or cast operators as implicit! I also use alias this to fake struct inheritance. I'd argue that I shouldn't be using alias this for either of these reasons. They're both ugly hacks in the face of language deficiency which really subvert the intent of the code, and ruin the editor/debug experience.
Jun 10 2019
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/9/2019 10:52 PM, Manu wrote:
 That is indeed the feature that necessitates my 'solution'
One possibility is that if the alias this is referring to a struct with no fields, it gets a 0 size. This will mimic the behavior of C++.
Jun 10 2019
parent Manu <turkeyman gmail.com> writes:
On Mon, Jun 10, 2019 at 2:55 AM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 6/9/2019 10:52 PM, Manu wrote:
 That is indeed the feature that necessitates my 'solution'
One possibility is that if the alias this is referring to a struct with no fields, it gets a 0 size. This will mimic the behavior of C++.
The struct exists as a member irrespective of the alias this. And at this point, you have implemented struct inheritance in absolute terms, but why would you prefer an ugly and unclear syntax to specify it? Inheritance syntax is blindingly obvious and easy to see, base-struct + alias this is awkward to spot; the base member could be anywhere, and the `alias this` may or may not be present (you have to go looking for it), and the line may not even be nearby...
Jun 10 2019
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/9/2019 10:52 PM, Manu wrote:
 That is indeed the feature that necessitates my 'solution',
Is it specifically for C++ interop, or you just want the zero size?
Jun 10 2019
parent reply Manu <turkeyman gmail.com> writes:
On Mon, Jun 10, 2019 at 11:30 PM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 6/9/2019 10:52 PM, Manu wrote:
 That is indeed the feature that necessitates my 'solution',
Is it specifically for C++ interop,
It occurs frequently in C++ interop, but that's only because most of my code exists in C++. I do, and will continue to use base structs in C++ and in D for exactly the same reasons. Every codebase I have ever worked in uses base struct's to DRY liberally, thousands of engineers think this is fine, and I've never heard anyone think it's unusual, or weird/problematic.
 or you just want the zero size?
A zero size solution removes the static if hack, but it doesn't remove the alias this hack. I want to remove both hacks. I can't stress enough that struct inheritance exists, it's extremely common, and we will continue to do it as a completely normal practice with no apology. Please don't make our code ugly and unnecessarily difficult to understand for no reason. If you fear misunderstanding with polymorphism, fear not; this is not C++, struct is strictly a value type, there is no vtable, no virtual, no override, etc... there would be compile errors at every turn if anyone ever confused struct inheritance for polymorphism. We have a HUGE advantage here...
Jun 11 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/11/2019 12:06 AM, Manu wrote:
 On Mon, Jun 10, 2019 at 11:30 PM Walter Bright via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 6/9/2019 10:52 PM, Manu wrote:
 That is indeed the feature that necessitates my 'solution',
Is it specifically for C++ interop,
It occurs frequently in C++ interop, but that's only because most of my code exists in C++. I do, and will continue to use base structs in C++ and in D for exactly the same reasons. Every codebase I have ever worked in uses base struct's to DRY liberally, thousands of engineers think this is fine, and I've never heard anyone think it's unusual, or weird/problematic.
 or you just want the zero size?
A zero size solution removes the static if hack, but it doesn't remove the alias this hack. I want to remove both hacks. I can't stress enough that struct inheritance exists, it's extremely common, and we will continue to do it as a completely normal practice with no apology. Please don't make our code ugly and unnecessarily difficult to understand for no reason. If you fear misunderstanding with polymorphism, fear not; this is not C++, struct is strictly a value type, there is no vtable, no virtual, no override, etc... there would be compile errors at every turn if anyone ever confused struct inheritance for polymorphism. We have a HUGE advantage here...
Can I ask again, in a different way, why do you need the 0 size?
Jun 11 2019
next sibling parent reply Manu <turkeyman gmail.com> writes:
On Tue, Jun 11, 2019 at 1:00 AM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 6/11/2019 12:06 AM, Manu wrote:
 On Mon, Jun 10, 2019 at 11:30 PM Walter Bright via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 6/9/2019 10:52 PM, Manu wrote:
 That is indeed the feature that necessitates my 'solution',
Is it specifically for C++ interop,
It occurs frequently in C++ interop, but that's only because most of my code exists in C++. I do, and will continue to use base structs in C++ and in D for exactly the same reasons. Every codebase I have ever worked in uses base struct's to DRY liberally, thousands of engineers think this is fine, and I've never heard anyone think it's unusual, or weird/problematic.
 or you just want the zero size?
A zero size solution removes the static if hack, but it doesn't remove the alias this hack. I want to remove both hacks. I can't stress enough that struct inheritance exists, it's extremely common, and we will continue to do it as a completely normal practice with no apology. Please don't make our code ugly and unnecessarily difficult to understand for no reason. If you fear misunderstanding with polymorphism, fear not; this is not C++, struct is strictly a value type, there is no vtable, no virtual, no override, etc... there would be compile errors at every turn if anyone ever confused struct inheritance for polymorphism. We have a HUGE advantage here...
Can I ask again, in a different way, why do you need the 0 size?
To handle base structs with no members.
Jun 11 2019
next sibling parent reply Exil <Exil gmall.com> writes:
On Tuesday, 11 June 2019 at 18:19:05 UTC, Manu wrote:
 On Tue, Jun 11, 2019 at 1:00 AM Walter Bright via Digitalmars-d 
 <digitalmars-d puremagic.com> wrote:
 On 6/11/2019 12:06 AM, Manu wrote:
 On Mon, Jun 10, 2019 at 11:30 PM Walter Bright via 
 Digitalmars-d <digitalmars-d puremagic.com> wrote:
 On 6/9/2019 10:52 PM, Manu wrote:
 That is indeed the feature that necessitates my 'solution',
Is it specifically for C++ interop,
It occurs frequently in C++ interop, but that's only because most of my code exists in C++. I do, and will continue to use base structs in C++ and in D for exactly the same reasons. Every codebase I have ever worked in uses base struct's to DRY liberally, thousands of engineers think this is fine, and I've never heard anyone think it's unusual, or weird/problematic.
 or you just want the zero size?
A zero size solution removes the static if hack, but it doesn't remove the alias this hack. I want to remove both hacks. I can't stress enough that struct inheritance exists, it's extremely common, and we will continue to do it as a completely normal practice with no apology. Please don't make our code ugly and unnecessarily difficult to understand for no reason. If you fear misunderstanding with polymorphism, fear not; this is not C++, struct is strictly a value type, there is no vtable, no virtual, no override, etc... there would be compile errors at every turn if anyone ever confused struct inheritance for polymorphism. We have a HUGE advantage here...
Can I ask again, in a different way, why do you need the 0 size?
To handle base structs with no members.
Kind of curious, what do you use a base class for that has no members, is a size of zero, and also has no vtable or virtual functions?
Jun 11 2019
parent reply Manu <turkeyman gmail.com> writes:
On Tue, Jun 11, 2019 at 12:35 PM Exil via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Tuesday, 11 June 2019 at 18:19:05 UTC, Manu wrote:
 On Tue, Jun 11, 2019 at 1:00 AM Walter Bright via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 6/11/2019 12:06 AM, Manu wrote:
 On Mon, Jun 10, 2019 at 11:30 PM Walter Bright via
 Digitalmars-d <digitalmars-d puremagic.com> wrote:
 On 6/9/2019 10:52 PM, Manu wrote:
 That is indeed the feature that necessitates my 'solution',
Is it specifically for C++ interop,
It occurs frequently in C++ interop, but that's only because most of my code exists in C++. I do, and will continue to use base structs in C++ and in D for exactly the same reasons. Every codebase I have ever worked in uses base struct's to DRY liberally, thousands of engineers think this is fine, and I've never heard anyone think it's unusual, or weird/problematic.
 or you just want the zero size?
A zero size solution removes the static if hack, but it doesn't remove the alias this hack. I want to remove both hacks. I can't stress enough that struct inheritance exists, it's extremely common, and we will continue to do it as a completely normal practice with no apology. Please don't make our code ugly and unnecessarily difficult to understand for no reason. If you fear misunderstanding with polymorphism, fear not; this is not C++, struct is strictly a value type, there is no vtable, no virtual, no override, etc... there would be compile errors at every turn if anyone ever confused struct inheritance for polymorphism. We have a HUGE advantage here...
Can I ask again, in a different way, why do you need the 0 size?
To handle base structs with no members.
Kind of curious, what do you use a base class for that has no members, is a size of zero, and also has no vtable or virtual functions?
Metaprogramming; it's a template argument. It might be the case that some method is expected to exist, but the particular implementation may or may not require any data. Virtuals are dead man, nobody uses OOP anymore. It's all about contracts and things like that. Base classes might be expected to have a method, and there may be a static assert to prove that some API is satisfied, but the implementation may want to have some additional state. This pattern doesn't exclusively apply to inheritance, but that's the case on trial.
Jun 11 2019
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 6/11/19 7:45 PM, Manu wrote:
 On Tue, Jun 11, 2019 at 12:35 PM Exil via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 Kind of curious, what do you use a base class for that has no
 members, is a size of zero, and also has no vtable or virtual
 functions?
Metaprogramming; it's a template argument.
Subtyping is not needed for metaprogramming. (C++'s awkward way of doing it overuses it.)
Jun 12 2019
parent reply Manu <turkeyman gmail.com> writes:
On Wed, Jun 12, 2019 at 11:40 PM Andrei Alexandrescu via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 6/11/19 7:45 PM, Manu wrote:
 On Tue, Jun 11, 2019 at 12:35 PM Exil via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 Kind of curious, what do you use a base class for that has no
 members, is a size of zero, and also has no vtable or virtual
 functions?
Metaprogramming; it's a template argument.
Subtyping is not needed for metaprogramming. (C++'s awkward way of doing it overuses it.)
Overuse of `alias this` is not any better. `alias this` is relatively much more awkward, and I would say objectively less easy to read and understand. You can't miss the base at the top of the struct, you know exactly where to look for it. With `alias this`, you have to scan the object from top to bottom to see if it's there, and then you have to take note what object is acting as the base and look for that and where it sits relative to other members, and then worry about the zero-size hack. That alone makes C++'s "awkward way of doing it" look golden, but then there's also the issues with the IDE tooling, and that there's only one `alias this` slot, which we might need for implicit conversion of some sort. What's so upsetting about C++'s approach? You say 'subtyping', but that sounds like you're talking about polymorphism again. Perhaps it would be better to say 'extending', which is really what it is applied to a struct. I think it's a simple and natural syntax to do a simple and natural operation like extending a struct. Look, I'm not actually married to C++'s approach (if you have a better idea), but I'm dreadfully bored of D's shitty approach, I've had a decade to try and think it's cool, but I actually think it's more shit than I thought it was 10 years ago. I used to think it was a cool idea; it's a 'cute' use of a novel feature in D which achieves a goal... and like, bugger C++ and it's stupid face, and anything that's not like C++ is cool, but I was wrong. It's a shitty thing to write over and over again and I hate it, having done it **A LOT**. I haven't generally made a fuss about this like other things because it's not a blocker in any way, but it's important to express how bored I am of the pattern. It's not better.
Jun 13 2019
parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Thursday, 13 June 2019 at 07:04:06 UTC, Manu wrote:
 On Wed, Jun 12, 2019 at 11:40 PM Andrei Alexandrescu via 
 Digitalmars-d <digitalmars-d puremagic.com> wrote:
 On 6/11/19 7:45 PM, Manu wrote:
 On Tue, Jun 11, 2019 at 12:35 PM Exil via Digitalmars-d 
 <digitalmars-d puremagic.com> wrote:
 Kind of curious, what do you use a base class for that has 
 no members, is a size of zero, and also has no vtable or 
 virtual functions?
Metaprogramming; it's a template argument.
Subtyping is not needed for metaprogramming. (C++'s awkward way of doing it overuses it.)
Overuse of `alias this` is not any better. `alias this` is relatively much more awkward, and I would say objectively less easy to read and understand. You can't miss the base at the top of the struct, you know exactly where to look for it. With `alias this`, you have to scan the object from top to bottom to see if it's there, and then you have to take note what object is acting as the base and look for that and where it sits relative to other members, and then worry about the zero-size hack. That alone makes C++'s "awkward way of doing it" look golden, but then there's also the issues with the IDE tooling, and that there's only one `alias this` slot, which we might need for implicit conversion of some sort. What's so upsetting about C++'s approach? You say 'subtyping', but that sounds like you're talking about polymorphism again. Perhaps it would be better to say 'extending', which is really what it is applied to a struct. I think it's a simple and natural syntax to do a simple and natural operation like extending a struct. Look, I'm not actually married to C++'s approach (if you have a better idea), but I'm dreadfully bored of D's shitty approach, I've had a decade to try and think it's cool, but I actually think it's more shit than I thought it was 10 years ago. I used to think it was a cool idea; it's a 'cute' use of a novel feature in D which achieves a goal... and like, bugger C++ and it's stupid face, and anything that's not like C++ is cool, but I was wrong. It's a shitty thing to write over and over again and I hate it, having done it **A LOT**. I haven't generally made a fuss about this like other things because it's not a blocker in any way, but it's important to express how bored I am of the pattern. It's not better.
Wouldn't it be better to allow structs to implement static only interfaces? -Alex
Jun 13 2019
parent reply Manu <turkeyman gmail.com> writes:
On Thu, Jun 13, 2019 at 7:55 AM 12345swordy via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 [...] It's not better.
Wouldn't it be better to allow structs to implement static only interfaces?
I don't know what that means? Like D `interface`, which is a pure vtable with no data members? That's the opposite of what I want... what is a 'static' interface?
Jun 13 2019
next sibling parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Thursday, 13 June 2019 at 22:12:37 UTC, Manu wrote:
 On Thu, Jun 13, 2019 at 7:55 AM 12345swordy via Digitalmars-d 
 <digitalmars-d puremagic.com> wrote:
 [...] It's not better.
Wouldn't it be better to allow structs to implement static only interfaces?
I don't know what that means? Like D `interface`, which is a pure vtable with no data members? That's the opposite of what I want... what is a 'static' interface?
Interface with only static functions and data members. No virtual function what so ever.
Jun 13 2019
parent reply Manu <turkeyman gmail.com> writes:
On Thu, Jun 13, 2019 at 4:05 PM 12345swordy via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Thursday, 13 June 2019 at 22:12:37 UTC, Manu wrote:
 On Thu, Jun 13, 2019 at 7:55 AM 12345swordy via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 [...] It's not better.
Wouldn't it be better to allow structs to implement static only interfaces?
I don't know what that means? Like D `interface`, which is a pure vtable with no data members? That's the opposite of what I want... what is a 'static' interface?
Interface with only static functions and data members. No virtual function what so ever.
Well... right. We're talking about struct's here. struct's don't have virtual functions.
Jun 13 2019
parent reply 12345swordy <alexanderheistermann gmail.com> writes:
On Thursday, 13 June 2019 at 23:28:37 UTC, Manu wrote:
 On Thu, Jun 13, 2019 at 4:05 PM 12345swordy via Digitalmars-d 
 <digitalmars-d puremagic.com> wrote:
 On Thursday, 13 June 2019 at 22:12:37 UTC, Manu wrote:
 On Thu, Jun 13, 2019 at 7:55 AM 12345swordy via 
 Digitalmars-d <digitalmars-d puremagic.com> wrote:
 [...] It's not better.
Wouldn't it be better to allow structs to implement static only interfaces?
I don't know what that means? Like D `interface`, which is a pure vtable with no data members? That's the opposite of what I want... what is a 'static' interface?
Interface with only static functions and data members. No virtual function what so ever.
Well... right. We're talking about struct's here. struct's don't have virtual functions.
I am saying that structs should be allow to inherent interfaces, provided that said interface only contains statics functions(with or without implementation) or data members. What you want is the ability to define a contract for the structs to inherited without any virtual functions.
Jun 13 2019
parent Manu <turkeyman gmail.com> writes:
On Thu, Jun 13, 2019 at 6:13 PM 12345swordy via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Thursday, 13 June 2019 at 23:28:37 UTC, Manu wrote:
 On Thu, Jun 13, 2019 at 4:05 PM 12345swordy via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On Thursday, 13 June 2019 at 22:12:37 UTC, Manu wrote:
 On Thu, Jun 13, 2019 at 7:55 AM 12345swordy via
 Digitalmars-d <digitalmars-d puremagic.com> wrote:
 [...] It's not better.
Wouldn't it be better to allow structs to implement static only interfaces?
I don't know what that means? Like D `interface`, which is a pure vtable with no data members? That's the opposite of what I want... what is a 'static' interface?
Interface with only static functions and data members. No virtual function what so ever.
Well... right. We're talking about struct's here. struct's don't have virtual functions.
I am saying that structs should be allow to inherent interfaces, provided that said interface only contains statics functions(with or without implementation) or data members.
I think that's much more weird than struct's inheriting a struct... an interface is everything other than a static non-virtual type... a struct is *exactly* that.
 What you want is the ability to define a contract for the structs
 to inherited without any virtual functions.
I would expect something different from a "contract"; that sounds like some expectations without some concrete details defined. And again, this is the opposite of that.
Jun 13 2019
prev sibling parent Random D user <no email.com> writes:
On Thursday, 13 June 2019 at 22:12:37 UTC, Manu wrote:
 what is a 'static' interface?
I would define it as something like this: static interface Processor { // Required methods for a struct void prepare(Options opts); void process(ubyte[] data); bool isReady(); Result get(); int integer; // No size, just a requirement } struct IncorrectProcessor : Processor { void prepare(Options opts) { ... } void doSomething() { ... } } // Implements all that's in the 'static interface' even the (public) data. struct CorrectProcessor : Processor { void prepare(Options opts) { ... } void process(ubyte[] data) { ... } bool isReady() { ... } Result get() { ... } int integer; void doSomething2() { ... } void doSomething3() { ... } } void myProcessorUseCase(Processor p) { p.prepare(opts); p.process(data); while(!p.isReady()) { ... } return p.get(); } // You can now call myProcessorUseCase with different types of processors with in different parts of code. No runtime type switching, no virtuals. myProcessorUseCase( incorrectProcessor ); // Error: 'incorrectProcessor' doesn't implement static interface 'Processor' correctly myProcessorUseCase( correctProcessor ); // OK ----- Basically, a static compile-time check that struct is of specific form. Kind of like compiling a different .c file for a shared/interface .h file for different builds, except not restricted to files and builds. No virtuals or anything. Compiling doesn't produce any artifacts just does the check. I've often wished for a feature like this. I suppose you can do template constraints, static ifs and meta, but honestly I find the result cumbersome and not very readable/debuggable.
Jun 14 2019
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/11/2019 11:19 AM, Manu wrote:
 Can I ask again, in a different way, why do you need the 0 size?
To handle base structs with no members.
For the 3rd time, why do you need it to have 0 size? I don't know what you mean by "handling" it.
Jun 12 2019
next sibling parent reply Manu <turkeyman gmail.com> writes:
On Wed, Jun 12, 2019 at 10:25 PM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 6/11/2019 11:19 AM, Manu wrote:
 Can I ask again, in a different way, why do you need the 0 size?
To handle base structs with no members.
For the 3rd time, why do you need it to have 0 size? I don't know what you mean by "handling" it.
I hate wasting 8 bytes needlessly. Almost every struct I write is carefully packed and aligned to 16, 32, or 64 bytes and allocated in pools... that's 50%, 25%, or 12.5% memory wastage. There's also ABI compatibility with existing C++ where this is super-common. Consider that user-supplied <Allocator> is a base of every STL container, that's one case where I've had to deploy the static if hack a lot. Those are absolutely not polymorphic types, and you couldn't make that mistake no matter how hard you squinted. They sometimes use a helper called a 'packed pair' in the STL, which is like std::pair (obviously another not-a-polymorphic-type), but where one or the other element can be zero-sized and not waste memory. It's an aggregation tool.
Jun 12 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/12/2019 11:29 PM, Manu wrote:
 On Wed, Jun 12, 2019 at 10:25 PM Walter Bright via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 6/11/2019 11:19 AM, Manu wrote:
 Can I ask again, in a different way, why do you need the 0 size?
To handle base structs with no members.
For the 3rd time, why do you need it to have 0 size? I don't know what you mean by "handling" it.
I hate wasting 8 bytes needlessly. Almost every struct I write is carefully packed and aligned to 16, 32, or 64 bytes and allocated in pools... that's 50%, 25%, or 12.5% memory wastage. There's also ABI compatibility with existing C++ where this is super-common. Consider that user-supplied <Allocator> is a base of every STL container, that's one case where I've had to deploy the static if hack a lot. Those are absolutely not polymorphic types, and you couldn't make that mistake no matter how hard you squinted. They sometimes use a helper called a 'packed pair' in the STL, which is like std::pair (obviously another not-a-polymorphic-type), but where one or the other element can be zero-sized and not waste memory. It's an aggregation tool.
Have you considered using template mixins? Since you're using a base struct merely as a namespace for some functions, that should work.
Jun 18 2019
next sibling parent reply Ethan <gooberman gmail.com> writes:
On Tuesday, 18 June 2019 at 09:15:42 UTC, Walter Bright wrote:
 On 6/12/2019 11:29 PM, Manu wrote:
 There's also ABI compatibility with existing C++ where this is
 super-common. Consider that user-supplied <Allocator> is a 
 base of
 every STL container, that's one case where I've had to deploy 
 the
 static if hack a lot.
Have you considered using template mixins? Since you're using a base struct merely as a namespace for some functions, that should work.
The ABI compatibility point really needs to be reiterated here. If the end goal, especially with things like D++, is to drive adoption of D by ensuring C++ interop is simple then that's now going to result in a static analysis tool to look at any given C++ struct and work out whether it should or shouldn't be converted to a mixin template. And introduce the dissonance of a C++ type not actually being a type in D.
Jun 18 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/18/2019 3:34 AM, Ethan wrote:
 Have you considered using template mixins? Since you're using a base struct 
 merely as a namespace for some functions, that should work.
The ABI compatibility point really needs to be reiterated here. If the end goal, especially with things like D++, is to drive adoption of D by ensuring C++ interop is simple then that's now going to result in a static analysis tool to look at any given C++ struct and work out whether it should or shouldn't be converted to a mixin template. And introduce the dissonance of a C++ type not actually being a type in D.
You can do this: mixin template BaseMembers() { void memberFunction() { ... } } struct Base { mixin BaseMembers!(); } struct Derived { mixin BaseMembers!(); // walla! zero size for "inheritance"! Base b; // when I want C++ 1 byte size } It's not a thing of beauty, but nothing connected with C++ is :-)
Jun 18 2019
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 18.06.19 21:58, Walter Bright wrote:
 On 6/18/2019 3:34 AM, Ethan wrote:
 Have you considered using template mixins? Since you're using a base 
 struct merely as a namespace for some functions, that should work.
The ABI compatibility point really needs to be reiterated here. If the end goal, especially with things like D++, is to drive adoption of D by ensuring C++ interop is simple then that's now going to result in a static analysis tool to look at any given C++ struct and work out whether it should or shouldn't be converted to a mixin template. And introduce the dissonance of a C++ type not actually being a type in D.
You can do this:   mixin template BaseMembers() {     void memberFunction() { ... }   }   struct Base {     mixin BaseMembers!();   }   struct Derived {     mixin BaseMembers!();  // walla! zero size for "inheritance"!     Base b;            // when I want C++ 1 byte size   } It's not a thing of beauty, but nothing connected with C++ is :-)
This works if Base and Derived are defined in the same module. Otherwise this does not do the correct thing, because mixin templates are analyzed in the scope into which they are mixed.
Jun 18 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/18/2019 5:01 PM, Timon Gehr wrote:
 This works if Base and Derived are defined in the same module. Otherwise this 
 does not do the correct thing, because mixin templates are analyzed in the
scope 
 into which they are mixed.
That is correct, but with some care you can make it work, even if the mixin template functions just forward to other functions which are evaluated in the BaseMembers' scope.
Jun 18 2019
parent reply Manu <turkeyman gmail.com> writes:
On Wed, Jun 19, 2019 at 10:45 AM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 6/18/2019 5:01 PM, Timon Gehr wrote:
 This works if Base and Derived are defined in the same module. Otherwise this
 does not do the correct thing, because mixin templates are analyzed in the
scope
 into which they are mixed.
That is correct, but with some care you can make it work, even if the mixin template functions just forward to other functions which are evaluated in the BaseMembers' scope.
I already have workarounds. That's not what this thread is about.
Jun 18 2019
parent John Colvin <john.loughran.colvin gmail.com> writes:
On Wednesday, 19 June 2019 at 01:10:56 UTC, Manu wrote:
 On Wed, Jun 19, 2019 at 10:45 AM Walter Bright via 
 Digitalmars-d <digitalmars-d puremagic.com> wrote:
 On 6/18/2019 5:01 PM, Timon Gehr wrote:
 This works if Base and Derived are defined in the same 
 module. Otherwise this does not do the correct thing, 
 because mixin templates are analyzed in the scope into which 
 they are mixed.
That is correct, but with some care you can make it work, even if the mixin template functions just forward to other functions which are evaluated in the BaseMembers' scope.
I already have workarounds. That's not what this thread is about.
Not just my opinion, but what I perceive to be expected procedure here: The merits of any new feature have to be argued in terms of net benefit over existing solutions. Just my opinion: The best feature design often comes from struggling with current features. You obviously have done a lot of the second, but it seems that you think "it's obviously better, the current solutions are just ugly hacks, everyone will laugh at us" is going to convince people enough w.r.t. the former. Obvious hyperbole is obvious, but hopefully you get my point?
Jun 25 2019
prev sibling parent Manu <turkeyman gmail.com> writes:
On Tue, Jun 18, 2019 at 7:20 PM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 6/12/2019 11:29 PM, Manu wrote:
 On Wed, Jun 12, 2019 at 10:25 PM Walter Bright via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 6/11/2019 11:19 AM, Manu wrote:
 Can I ask again, in a different way, why do you need the 0 size?
To handle base structs with no members.
For the 3rd time, why do you need it to have 0 size? I don't know what you mean by "handling" it.
I hate wasting 8 bytes needlessly. Almost every struct I write is carefully packed and aligned to 16, 32, or 64 bytes and allocated in pools... that's 50%, 25%, or 12.5% memory wastage. There's also ABI compatibility with existing C++ where this is super-common. Consider that user-supplied <Allocator> is a base of every STL container, that's one case where I've had to deploy the static if hack a lot. Those are absolutely not polymorphic types, and you couldn't make that mistake no matter how hard you squinted. They sometimes use a helper called a 'packed pair' in the STL, which is like std::pair (obviously another not-a-polymorphic-type), but where one or the other element can be zero-sized and not waste memory. It's an aggregation tool.
Have you considered using template mixins? Since you're using a base struct merely as a namespace for some functions, that should work.
I mean, I love macro's as much as the next guy, and they're not at all embarrassing.
Jun 18 2019
prev sibling parent reply Manu <turkeyman gmail.com> writes:
On Wed, Jun 12, 2019 at 11:29 PM Manu <turkeyman gmail.com> wrote:
 On Wed, Jun 12, 2019 at 10:25 PM Walter Bright via Digitalmars-d
 <digitalmars-d puremagic.com> wrote:
 On 6/11/2019 11:19 AM, Manu wrote:
 Can I ask again, in a different way, why do you need the 0 size?
To handle base structs with no members.
For the 3rd time, why do you need it to have 0 size? I don't know what you mean by "handling" it.
I hate wasting 8 bytes needlessly. Almost every struct I write is carefully packed and aligned to 16, 32, or 64 bytes and allocated in pools... that's 50%, 25%, or 12.5% memory wastage. There's also ABI compatibility with existing C++ where this is super-common. Consider that user-supplied <Allocator> is a base of every STL container, that's one case where I've had to deploy the static if hack a lot. Those are absolutely not polymorphic types, and you couldn't make that mistake no matter how hard you squinted. They sometimes use a helper called a 'packed pair' in the STL, which is like std::pair (obviously another not-a-polymorphic-type), but where one or the other element can be zero-sized and not waste memory. It's an aggregation tool.
What are we so afraid of? I don't understand the resistance to this *at all*... people talk about slicing, which is a pretty worthless baseline argument; it's totally possible in C++ and I've never heard a single case of anyone ever having that problem in my life, at work, at home, or even on the internet (yes, I'm sure I would find if I deliberately went looking). I'm only aware the concept has a name because I've seen it written in a book somewhere. It's 99.9999% not-even-remotely-a-problem, it's only a 'theoretical' problem. What if we dis-allowed implicit casting structs to base type? I mean, nothing else about it is polymorphic in nature... not-allowing implicit casting to base type would not cause problems in any use I can imagine. This should obviously be a compile error: struct A { int x; } struct B : A { int y; } B b; A a = b; // <- obviously an error
Jun 12 2019
next sibling parent XavierAP <n3minis-git yahoo.es> writes:
On Thursday, 13 June 2019 at 06:43:44 UTC, Manu wrote:
 What are we so afraid of? I don't understand the resistance to 
 this *at all*... people talk about slicing, which is a pretty 
 worthless baseline argument; it's totally possible in C++ and 
 I've never heard a single case of anyone ever having that 
 problem in my life, at work, at home, or even on the internet 
 (yes, I'm sure I would find if I deliberately went looking). 
 I'm only aware the concept has a name because I've seen it 
 written in a book somewhere. It's 99.9999% 
 not-even-remotely-a-problem, it's only a 'theoretical' problem.

 What if we dis-allowed implicit casting structs to base type? I 
 mean, nothing else about it is polymorphic in nature... 
 not-allowing implicit casting to base type would not cause 
 problems in any use I can imagine.

 This should obviously be a compile error:
   struct A { int x; }
   struct B : A { int y; }
   B b;
   A a = b; // <- obviously an error
Several topics have come up that imho all may have merit and bit size (related to Manu's application but in general having unsafe consequences. Regarding the OP abut struct inheritance, I think we need to specify the request better, and then discuss more objectively its pros and cons, and if there are advantages, what restrictions are needed to avoid pitfalls. We are also getting confused with words: we aren't placing the same meaning on the same words, specially "polymorphism". I will use it with the meaning of one type used in place of another (i.e. subtyping), while Manu seems to restrict the term to when this is late/dynamically bound. I'm not clear how much polymorphism (not only dynamic) this would carry or we'd like it to? Not just implicit casting or copy constructors (again let's be general and leave the OP application aside), but what about passing derived structs by ref or pointer? And What about destructors? After all a derived struct would indeed have the data and methods of the base one, so it is open to consideration that it could be (statically) polymorphic, either by ref or by copy or both. I am in favor of not being dogmatic, but if D introduced this it would be treading unproven territory; so it deserves a lot of thinking so that we don't introduce pitfalls into the language; but I am in favor of D having more good features and DRY. In C++ everything is allowed of course, but a virtual destructor is absolutely advised when inheriting. (Besides the fact that dynamic polymorphism and OOP themselves are nowadays out of fashion with the dominant C++ and D crowds.) Most times this question is discussed in the context of inheriting STL containers, which goes at least against the design intent of the STL in particular; but again our question is more general. .NET has quite the same kind of structs as D, and forbids structs to inherit custom types (although they can implement interfaces!). The CLI specification says only this is to "avoid dealing with the complications of value slicing ... allow for more efficient implementation without severely compromising functionality."
Jun 13 2019
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 13.06.19 08:43, Manu wrote:
 What are we so afraid of? I don't understand the resistance to this
 *at all*...
I think a lot of the default opposition to new features (especially if they can be implemented with a moderate amount of user code) is about DMD compiler complexity, about adding new things to a code base which already suffers quite a bit from incidental complexity. (E.g., I learned last week that there are multiple different ways to infer function attributes. Some of them are quite broken, but I wasn't able to get rid of the special and broken `pure` inference without breaking the test suite, within the allotted time.)
 people talk about slicing, which is a pretty worthless
 baseline argument; it's totally possible in C++ and I've never heard a
 single case of anyone ever having that problem in my life, at work, at
 home, or even on the internet (yes, I'm sure I would find if I
 deliberately went looking). I'm only aware the concept has a name
 because I've seen it written in a book somewhere. It's 99.9999%
 not-even-remotely-a-problem, it's only a 'theoretical' problem.
 ...
Slicing is only a problem if you have a vtable.
Jun 13 2019
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/13/2019 5:03 AM, Timon Gehr wrote:
 I think a lot of the default opposition to new features (especially if they
can 
 be implemented with a moderate amount of user code) is about DMD compiler 
 complexity, about adding new things to a code base which already suffers quite
a 
 bit from incidental complexity.
D has a TON of features. And yet every single day, people demand ever more features. How can this possibly work without turning D (and the compiler) into a unusable mess. I have no choice but to constantly say "no". As for struct inheritance, it seems what Manu really wants isn't struct inheritance at all, it is zero sized structs having zero size when they are used as fields. First off, he has a workaround for it. I agree it's not very pretty, but it works and he is not blocked. That moves it way down on the priority list. Secondly, there are template mixins, which have zero size.
Jun 18 2019
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 18.06.19 11:23, Walter Bright wrote:
 On 6/13/2019 5:03 AM, Timon Gehr wrote:
 I think a lot of the default opposition to new features (especially if 
 they can be implemented with a moderate amount of user code) is about 
 DMD compiler complexity, about adding new things to a code base which 
 already suffers quite a bit from incidental complexity.
D has a TON of features. And yet every single day, people demand ever more features. How can this possibly work without turning D (and the compiler) into a unusable mess.
As far as I can tell, a lot of the incidental complexity is introduced in bug fixes. Furthermore, the issue is: - features whose implementation is spread all over the code base without an easy way to find all components of their implementation. - features that should work the same (e.g. nogc inference and pure inference) that have a different and incompatible implementation.
 I have no choice but to constantly say "no".
 ...
I agree it is a useful default stance.
Jun 18 2019
parent Walter Bright <newshound2 digitalmars.com> writes:
On 6/18/2019 5:07 AM, Timon Gehr wrote:
 As far as I can tell, a lot of the incidental complexity is introduced in bug 
 fixes.
I've caught a lot of PRs for fixes that were incorrect. A lot slip by me.
 Furthermore, the issue is:
 - features whose implementation is spread all over the code base without an
easy 
 way to find all components of their implementation.
You're absolutely right on that. I really want to encapsulate this stuff - most recently https://github.com/dlang/dmd/pull/9993
 - features that should work the same (e.g.  nogc inference and pure inference) 
 that have a different and incompatible implementation.
Generally that is the result of incremental accretion of features.
Jun 18 2019
prev sibling next sibling parent reply Manu <turkeyman gmail.com> writes:
On Tue, Jun 18, 2019 at 7:25 PM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 6/13/2019 5:03 AM, Timon Gehr wrote:
 I think a lot of the default opposition to new features (especially if they can
 be implemented with a moderate amount of user code) is about DMD compiler
 complexity, about adding new things to a code base which already suffers quite
a
 bit from incidental complexity.
D has a TON of features. And yet every single day, people demand ever more features. How can this possibly work without turning D (and the compiler) into a unusable mess. I have no choice but to constantly say "no".
I'm pretty much just into feature uniformity at this stage. D's biggest problem from my perspective is "weird shit", mostly non-uniform arbitrary limitations and strange edge cases. And also that shared doesn't work.
 As for struct inheritance, it seems what Manu really wants isn't struct
 inheritance at all, it is zero sized structs having zero size when they are
used
 as fields.
As base structs specifically. I really want struct inheritance, and zero sized fields does happen to be one thing that falls out of that. There are 2 things I hate about it; one is the crappy static-if hack required to deal with zero-sized bases, the other is that I have to use `alias this` to describe a base. alias this *should* be a very niche feature, but instead we abuse it in lieu of struct inheritance, and no way to perform implicit conversion. In both cases it's a gross code smell, and I hate it.
 First off, he has a workaround for it. I agree it's not very pretty, but it
 works and he is not blocked. That moves it way down on the priority list.
I agree. The thread was just to re-iterate my strong discontent, and frankly, boredom with the situation.
 Secondly, there are template mixins, which have zero size.
That's feels even worse than my crappy hack to me.
Jun 18 2019
next sibling parent reply Olivier FAURE <couteaubleu gmail.com> writes:
On Tuesday, 18 June 2019 at 12:17:36 UTC, Manu wrote:
 As base structs specifically. I really want struct inheritance, 
 and
 zero sized fields does happen to be one thing that falls out of 
 that.
 There are 2 things I hate about it; one is the crappy static-if 
 hack
 required to deal with zero-sized bases, the other is that I 
 have to
 use `alias this` to describe a base.
 alias this *should* be a very niche feature, but instead we 
 abuse it
 in lieu of struct inheritance, and no way to perform implicit
 conversion. In both cases it's a gross code smell, and I hate 
 it.
It might be because I write a lot less generic code than you do, but I really don't understand why you lend so much importance to implicit conversion. XavierAP's question is particularly relevant here:
 What is the benefit of

 	struct A { void fun(); };
 	struct B :A {};

 	B b;
 	b.fun;

 compared to

 	struct A {};
 	struct B { A a; };

 	B b;
 	b.a.fun;
Like, seriously, I might be missing something, but I don't get it. Can you cite some use case where inheritance is convenient but composition isn't? Ideally an actual use case that came up in a project?
Jun 18 2019
parent XavierAP <n3minis-git yahoo.es> writes:
On Tuesday, 18 June 2019 at 18:39:41 UTC, Olivier FAURE wrote:
 On Tuesday, 18 June 2019 at 12:17:36 UTC, Manu wrote:
 alias this *should* be a very niche feature, but instead we 
 abuse it
 in lieu of struct inheritance, and no way to perform implicit
 conversion. In both cases it's a gross code smell, and I hate 
 it.
It might be because I write a lot less generic code than you do, but I really don't understand why you lend so much importance to implicit conversion. XavierAP's question is particularly relevant here:
 What is the benefit of

 	struct A { void fun(); }
 	struct B :A {}

 	B b;
 	b.fun;

 compared to

 	struct A {}
 	struct B { A a; }

 	B b;
 	b.a.fun;
Like, seriously, I might be missing something, but I don't get it. Can you cite some use case where inheritance is convenient but composition isn't? Ideally an actual use case that came up in a project?
I'm not against struct inheritance... Even though I don't get or haven't looked into Manu's application, and I rather try to understand the pros and cons in general, my impression is that he knows what he's talking about and has likely considered plenty of implications elsewhere, but he's tired of discussing over again. :p In very general terms there are potential risks when things happen implicitly. A new feature with such effects is likelier to have unintended consequences at least in corner cases. That's where my questions were coming from. But my initial guess is that there might be nothing wrong with struct inheritance, at least if the value semantics are preserved. And I do agree with Manu that alias this should be a very niche feature, and we are using it for structs as clumsier, dirtier and more verbose way to get the regular (static) polymorphism that we'd better implement by inheritance if possible. Walter himself has admitted (in the multiple alias this thread) that alias this can be equivalent to inheritance (and that's argument enough why multiple's bad). Something else in these discussions, the current fashion in CS is that inheritance is bad and you should use composition instead every single time -- just like the past OOP fad said the opposite against C. I agree that too deep or extensive inheritance hierarchies are a bad pattern, but inheritance isn't bad in itself or in moderation. And again this discussion gets mixed up with C++ people who design classes inheriting from the STL. Since it's probably a bad idea to extend a library following the opposite architecture patterns and design intent of the library, it's probably a bad idea to inherit from the STL (and the lack of virtual constructors doesn't help), but this get mixed up with the idea that inheriting itself would be always a bad idea for any architecture. And the fact that the STL was not made with an (even non virtual) inheritance architecture (*ahem* std::basic_string *ahem*) also makes inheritance less popular -- nowadays in comparison with composition, even though STL uses templates not composition.
Jun 19 2019
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/18/2019 5:17 AM, Manu wrote:

The more I've read about and studied concurrent programming issues, I tend to
Jun 18 2019
parent Manu <turkeyman gmail.com> writes:
On Wed, Jun 19, 2019 at 5:55 AM Walter Bright via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On 6/18/2019 5:17 AM, Manu wrote:

The more I've read about and studied concurrent programming issues, I tend to
On the plus side, a working shared solution is only a couple of lines away. We don't need anything fancy, actually less fancy than how it is now; just remove read/write, and let us see what we can do with the library (a lot). If/when we run into limitations with regards to library tooling, we can see how it looks from there.
Jun 18 2019
prev sibling parent Olivier FAURE <couteaubleu gmail.com> writes:
On Tuesday, 18 June 2019 at 09:23:45 UTC, Walter Bright wrote:
 As for struct inheritance, it seems what Manu really wants 
 isn't struct inheritance at all, it is zero sized structs 
 having zero size when they are used as fields.
Quick question, have you considered the following syntax: align(0) struct MyEmptyStruct {}; or some other means of saying "I don't intend to take this struct's address, I don't mind if it takes zero bytes of space"?
Jun 18 2019
prev sibling parent reply Exil <Exil gmall.com> writes:
On Thursday, 13 June 2019 at 06:43:44 UTC, Manu wrote:
 This should obviously be a compile error:
   struct A { int x; }
   struct B : A { int y; }
   B b;
   A a = b; // <- obviously an error
It doesn't become so obvious when you can implicitly convert to the base type. A foo(ref A a) { return a; } B b; A a = foo(b);
Jun 13 2019
parent reply Manu <turkeyman gmail.com> writes:
On Thu, Jun 13, 2019 at 7:35 AM Exil via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Thursday, 13 June 2019 at 06:43:44 UTC, Manu wrote:
 This should obviously be a compile error:
   struct A { int x; }
   struct B : A { int y; }
   B b;
   A a = b; // <- obviously an error
It doesn't become so obvious when you can implicitly convert to the base type. A foo(ref A a) { return a; } B b; A a = foo(b);
So don't implicitly convert to the base type? B extends A, but I don't think it's a kind of A as for polymorphic classes.
Jun 13 2019
next sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Thursday, 13 June 2019 at 22:13:51 UTC, Manu wrote:

 So don't implicitly convert to the base type?
 B extends A, but I don't think it's a kind of A as for 
 polymorphic classes.
So you want class Foo : BaseClass {} and struct Bar : BaseStruct {} to produce two different results?
Jun 13 2019
parent reply Manu <turkeyman gmail.com> writes:
On Thu, Jun 13, 2019 at 9:20 PM Mike Parker via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Thursday, 13 June 2019 at 22:13:51 UTC, Manu wrote:

 So don't implicitly convert to the base type?
 B extends A, but I don't think it's a kind of A as for
 polymorphic classes.
So you want class Foo : BaseClass {} and struct Bar : BaseStruct {} to produce two different results?
Well, one's a class and the other's a struct, and that means what it means.
Jun 13 2019
parent Mike Parker <aldacron gmail.com> writes:
On Friday, 14 June 2019 at 05:59:25 UTC, Manu wrote:

 So you want

 class Foo : BaseClass {}

 and

 struct Bar : BaseStruct {}

 to produce two different results?
Well, one's a class and the other's a struct, and that means what it means.
It means inconsistency, IMO, and a new violation of the principle of least astonishment. I have nothing against the behavior you want, but if it's going to behave differently from polymorphic inheritance, then it should look different.
Jun 14 2019
prev sibling parent reply XavierAP <n3minis-git yahoo.es> writes:
On Thursday, 13 June 2019 at 22:13:51 UTC, Manu wrote:
 So don't implicitly convert to the base type?
 B extends A, but I don't think it's a kind of A as for 
 polymorphic classes.
What is the benefit of struct A { void fun(); }; struct B :A {}; B b; b.fun; compared to struct A {}; struct B { A a; }; B b; b.a.fun; Less typing is good but stuff happening magically/unexpectedly for a neutral reader of the code (e.g. implicit casts) is not. And if on the other hand, !is(B:A), then what is the advantage, and the use case? Your OP pattern may have good alternatives that don't repeat code either, but produce more explicitly readable code?
Jun 14 2019
parent XavierAP <n3minis-git yahoo.es> writes:
On Friday, 14 June 2019 at 14:47:15 UTC, XavierAP wrote:
 On Thursday, 13 June 2019 at 22:13:51 UTC, Manu wrote:
 So don't implicitly convert to the base type?
 B extends A, but I don't think it's a kind of A as for 
 polymorphic classes.
Less typing is good but stuff happening magically/unexpectedly for a neutral reader of the code (e.g. implicit casts) is not. And if on the other hand, !is(B:A), then what is the advantage, and the use case?
Moreover, even if your inheritance did not produce (static) polymorphism in the hard sense i.e. !is(B:A), it would in the duck-typed sense, in templates! So inheritance without polymorphism is inconsistent. struct A { void fun(); }; struct B :A {}; void gun(A x) { x.fun; } void hun(T)(T x) { x.fun; } B b; b.gun; // error? b.hun; // ok
Jun 14 2019
prev sibling parent reply "Nick Sabalausky (Abscissa)" <SeeWebsiteToContactMe semitwist.com> writes:
On 6/11/19 3:56 AM, Walter Bright wrote:
 
 Can I ask again, in a different way, why do you need the 0 size?
 
In games programming, you frequently have (at least) one of two things: A. A limited-resource embedded system that you're wringing every last bit of memory and performance out of. B. At least several tens of gigabytes worth of content. Either way, this makes *ANY* wasted per-instance overhead unacceptable.
Jun 11 2019
parent Walter Bright <newshound2 digitalmars.com> writes:
On 6/11/2019 9:56 PM, Nick Sabalausky (Abscissa) wrote:
 Either way, this makes *ANY* wasted per-instance overhead unacceptable.
I'm curious what Manu's reason is. Does he have billions of them?
Jun 12 2019
prev sibling next sibling parent Dejan Lekic <dejan.lekic gmail.com> writes:
On Sunday, 9 June 2019 at 08:05:34 UTC, Manu wrote:
 Imagine if we could just write:

 struct DerivedStruct : BaseStruct
 {
     // derived members
     //...
 }

 Just imagine!
+1 I dream of being able to do struct DerivedStruct: SomeInterface, but what you are asking is also something I was wishing for years...
Jun 10 2019
prev sibling next sibling parent Radu <void null.pt> writes:
On Sunday, 9 June 2019 at 08:05:34 UTC, Manu wrote:
 I am really really tired of this pattern:

 struct DerivedStruct
 {
     static if (BaseStruct.tupleof.length > 0)
         BaseStruct base;
     else
         ref inout(BaseStruct) base() inout { return
 *cast(inout(BaseStruct)*)&this; }
     alias base this;

     // derived members
     //...
 }

 Imagine if we could just write:

 struct DerivedStruct : BaseStruct
 {
     // derived members
     //...
 }

 Just imagine!
I like the idea! I use the `alias this` idiom everywhere, and it is not too evident to new people coming from other languages, there is an overhead in explaining and understanding what that thing does, which is simple by itself. However, I do see some issues with using the existing inheritance syntax, as it creates some confusions and maybe some false expectations. I would like to have something like: struct Derived mixin Base { } which is more clear in the intent and reuses a well established concept in the language.
Jun 11 2019
prev sibling next sibling parent reply bioinfornatics <bioinfornatics fedoraproject.org> writes:
On Sunday, 9 June 2019 at 08:05:34 UTC, Manu wrote:
 I am really really tired of this pattern:

 struct DerivedStruct
 {
     static if (BaseStruct.tupleof.length > 0)
         BaseStruct base;
     else
         ref inout(BaseStruct) base() inout { return
 *cast(inout(BaseStruct)*)&this; }
     alias base this;

     // derived members
     //...
 }

 Imagine if we could just write:

 struct DerivedStruct : BaseStruct
 {
     // derived members
     //...
 }

 Just imagine!
That is a good idea (ahem… )but what about multiple derived classes ? I think the composition approach could help us by the use of the delegator concept from Ruby. With a such approach a struct can: - own multiple base/child struct/component - choose which method to forward to the base/child struct/component So at the and a such feature could be a sugar syntax of template Proxy: https://dlang.org/library/std/typecons/proxy.html Reference: - ruby delegator: https://blog.lelonek.me/how-to-delegate-methods-in-ruby-a7a71b077d99 - ruby delagator all supported methods: https://rubyreferences.github.io/rubyref/stdlib/patterns/delegate.html As Example struct Person { … int get_age(){…} } struct Career{ … int get_experience(){…} } struct Employee{ delegate: get_age, to: _person delegate: get_experience, to: _career immutable Person _person; immutable Career _career; this( immutable Person person , immutable Career career ){ _person = person; _career = career; } }
Jun 21 2019
parent reply Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Friday, 21 June 2019 at 22:19:12 UTC, bioinfornatics wrote:
 struct Employee{
   delegate: get_age, to: _person
   delegate: get_experience, to: _career

   immutable Person _person;
   immutable Career _career;
Listing all methods isn't very helpful, if that is the suggestion. Then a more consistent syntax would be struct Employee { alias getage = _person.getage; alias get_experience = _career.get_experience; Yet, that does not solve the MI problems. Some are: 1. What to do if you both superclasses has a length() method. 2. What do do if both superclasses has used the same method name for unrelated concepts? If there is no overlap semantically then problem 1 and 2 can be resolved with a cast to the super class, so that you get the right method. However, what if the subclass has to modify them to retain the correct semantics? Which is really what MI problems are about. Then you need som way of overloading a function that calls a specific attribute of a struct. So you need overloading on attributes, not only overloading on types, yet you also need a reference to the parent struct. So you need to be able to do something like int length(SubStruct.attributename* this) { this.parent } Ola.
Jun 21 2019
next sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= <ola.fosheim.grostad gmail.com> writes:
On Saturday, 22 June 2019 at 06:29:43 UTC, Ola Fosheim Grøstad 
wrote:
 Then you need som way of overloading a function that calls a 
 specific attribute of  a struct. So you need overloading on 
 attributes, not only overloading on types, yet you also need a 
 reference to the parent struct.

 So you need to be able to do something like

 int length(SubStruct.attributename* this) {
     this.parent
 }
In other words, you need static virtual methods. Perhaps a better syntax would be something along the lines of: struct SubClass { int _length; inherit SuperClass1 { int length(){ this.super.length() + this.parent._length} } attribute1; inherit SuperClass2 { int length(){ this.super.length() - this.parent._length} } attribute2; } It is difficult to find a syntax that isn't confusing. Probably better to do it with interfaces.
Jun 21 2019
prev sibling next sibling parent bioinfornatics <bioinfornatics fedoraproject.org> writes:
On Saturday, 22 June 2019 at 06:29:43 UTC, Ola Fosheim Grøstad 
wrote:
 On Friday, 21 June 2019 at 22:19:12 UTC, bioinfornatics wrote:
 struct Employee{
   delegate: get_age, to: _person
   delegate: get_experience, to: _career

   immutable Person _person;
   immutable Career _career;
Listing all methods isn't very helpful, if that is the suggestion. Then a more consistent syntax would be struct Employee { alias getage = _person.getage; alias get_experience = _career.get_experience; Yet, that does not solve the MI problems. Some are: 1. What to do if you both superclasses has a length() method. 2. What do do if both superclasses has used the same method name for unrelated concepts? If there is no overlap semantically then problem 1 and 2 can be resolved with a cast to the super class, so that you get the right method. However, what if the subclass has to modify them to retain the correct semantics? Which is really what MI problems are about. Then you need som way of overloading a function that calls a specific attribute of a struct. So you need overloading on attributes, not only overloading on types, yet you also need a reference to the parent struct. So you need to be able to do something like int length(SubStruct.attributename* this) { this.parent } Ola.
Yes you are right, previously I show one sample case, when you want to choose which method will be forwarded to the component. If you need to forward all child methods from the base struct you can use the template proxy from dlang or alias this. For MI case, as here is a composition: (i)a compiler's warning could be raised if method shadow another one (ii) a sugar syntax to "alias" to synonym method name delegate: Person.size, to: size delegate: Career.size, to: length (iii) a sugar syntax to forward all delegate: Person Is an example of syntax using annotation is possible to
Jun 21 2019
prev sibling parent reply XavierAP <n3minis-git yahoo.es> writes:
If we want multiple inheritance or even virtual methods in 
structs, completely new and severe problems appear. And I don't 
think these patterns have utility that justifies these problems 
or even their existence alone.

Single struct inheritance with no virtual methods presents in my 
opinion no problems, and it would be in many cases a better, more 
concise, boilerplate-less, clearer, cleaner way to accomplish the 
type extension and not repeating oneself that currently we can do 
only by composition and explicit forwarding, alias this, or 
macro-ish template mixins.

On Saturday, 22 June 2019 at 06:29:43 UTC, Ola Fosheim Grøstad 
wrote:
 On Friday, 21 June 2019 at 22:19:12 UTC, bioinfornatics wrote:
 struct Employee{
   delegate: get_age, to: _person
   delegate: get_experience, to: _career

   immutable Person _person;
   immutable Career _career;
Listing all methods isn't very helpful, if that is the suggestion. Then a more consistent syntax would be struct Employee { alias getage = _person.getage; alias get_experience = _career.get_experience;
Explicit forwarding is already/always possible. These syntaxes are not so more concise than auto getAge() { return _person.getAge(); } So I'd see no point in burdening D with adding such candy features.
Jun 22 2019
parent reply bioinfornatics <bioinfornatics fedoraproject.org> writes:
On Saturday, 22 June 2019 at 07:10:26 UTC, XavierAP wrote:
 If we want multiple inheritance or even virtual methods in 
 structs, completely new and severe problems appear. And I don't 
 think these patterns have utility that justifies these problems 
 or even their existence alone.

 Single struct inheritance with no virtual methods presents in 
 my opinion no problems, and it would be in many cases a better, 
 more concise, boilerplate-less, clearer, cleaner way to 
 accomplish the type extension and not repeating oneself that 
 currently we can do only by composition and explicit 
 forwarding, alias this, or macro-ish template mixins.

 On Saturday, 22 June 2019 at 06:29:43 UTC, Ola Fosheim Grøstad 
 wrote:
 On Friday, 21 June 2019 at 22:19:12 UTC, bioinfornatics wrote:
 struct Employee{
   delegate: get_age, to: _person
   delegate: get_experience, to: _career

   immutable Person _person;
   immutable Career _career;
Listing all methods isn't very helpful, if that is the suggestion. Then a more consistent syntax would be struct Employee { alias getage = _person.getage; alias get_experience = _career.get_experience;
Explicit forwarding is already/always possible. These syntaxes are not so more concise than auto getAge() { return _person.getAge(); } So I'd see no point in burdening D with adding such candy features.
Thanks for your insight. So I have a question what solve the struct inheritance which can not be done by the use of method forwarding? To me the template proxy is the way to go. They are no needs to extend the dlang syntax. Case 1: too few people know it Case 2: struct inheritance solve things that can not be done using the proxy https://dlang.org/library/std/typecons/proxy.html
Jun 24 2019
parent reply XavierAP <n3minis-git yahoo.es> writes:
On Monday, 24 June 2019 at 16:13:05 UTC, bioinfornatics wrote:
 Thanks for your insight.
 So I have a question what solve the struct inheritance which 
 can not be done by the use of method forwarding?
Less typing (cleaner, better code) and subtyping/polymorphism. Of course every language feature could be implemented from scratch in C. But if we're using other languages it's because their features are useful.
 To me the template proxy is the way to go. They are no needs to 
 extend the dlang syntax.

 Case 1: too few people know it
 Case 2: struct inheritance solve things that can not be done 
 using the proxy

 https://dlang.org/library/std/typecons/proxy.html
Proxy behaves (is purposely designed to behave) quite differently from inheritance... But to answer the general proposition that it's best to eliminate inheritance and implement it from templates, I think it makes no sense in principle because templates are more complex, both in code and under the hood (of the compiler specially). Inheritance (without virtual/dynamic dispatch) is as simple as aligning a few bytes together with the base type's ones. There are fads in patterns, and nowadays people are against inheritance. Little ago templates were the answer to everything, just like OOP was in the 90s (of course templates are extremely useful). Their use pushed compilation times and code readability out of orbit. Nowadays quite some people are reacting by going back to C, plain composition, and their dogma is that dangling pointers are the least problem. D was purposely created not to follow one single paradigm, but to offer all modern, proven ones to discerning programmers.
Jun 24 2019
parent reply Meta <jared771 gmail.com> writes:
On Monday, 24 June 2019 at 21:13:17 UTC, XavierAP wrote:
 Nowadays quite some people are reacting by going back to C, 
 plain composition, and their dogma is that dangling pointers are
 the least problem.
These people are insane. Long compile times and high compile memory usage create problems for developers. Dangling pointers create problems for developers AND users (very nasty problems at that). C cannot die quickly enough. We should be working to kill it, not bring about its resurgence.
Jun 24 2019
parent Exil <Exil gmall.com> writes:
On Monday, 24 June 2019 at 21:20:21 UTC, Meta wrote:
 On Monday, 24 June 2019 at 21:13:17 UTC, XavierAP wrote:
 Nowadays quite some people are reacting by going back to C, 
 plain composition, and their dogma is that dangling pointers 
 are
 the least problem.
These people are insane. Long compile times and high compile memory usage create problems for developers. Dangling pointers create problems for developers AND users (very nasty problems at that). C cannot die quickly enough. We should be working to kill it, not bring about its resurgence.
C++ is more so the one with how long it takes to compile. C isn't that bad. I don't see anything replacing C, there are a number of different languages that could but they are all fragmented. DMD can eat a lot of memory, it also isn't very efficient. static foreach(i; 0 .. 0xFFFF) {} the above code eats up over 12 GB of RAM. That's all my free RAM + whatever it is using on my drive. It also takes a REALLY long time to execute, at least an hour. I gave up after that. Spends almost all of it's time doing Array operations. DMD has a really weird implementation of Array, looks like it was implemented possibly in D1 days. Reserve(int) is additive, sure fine w/e. Passing around arrays as points, so need to dereference them to actually use them... Then you have insert(Range) that does a reserve(), which allocates new memory, copies all the data. Then the insert moves all the data it just copied. Sure the code is simpler but it has horrible performance, especially when there is a loop that is constantly adding and removing from the front/middle of the array.
Jun 24 2019
prev sibling parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Sunday, 9 June 2019 at 08:05:34 UTC, Manu wrote:
 Imagine if we could just write:

 struct DerivedStruct : BaseStruct
 {
     // derived members
     //...
 }

 Just imagine!
Mhmm... I'd think struct inheritance would follow the original C++'s ideology. So probably: 1) No Virtual functions. All functions are FINAL (replacing) 2) No polymorphism. 3) Limited casting (Casting up/back is easier than casting down, with space limitations in mind), though casting could simply be disallowed but that seems unlikely when you get low level. In such a case you could build a base struct and then add onto it for several structs behaviors. Being able to avoid using the GC as much as possible that would help. Also most of the functions/structures are basically copy/pasted with overriding functions completely replacing the previous ones. I've been wanting this for a while. I had a prototype set of templates to do this very thing! But... it no longer works, hasn't since... oh i don't remember. I'd have to pull the old files out... I'd probably ask a debugging feature where you could output the new finalized struct in it's full source code or maybe rather just it's API specifying what functions/variables come from where, so you get a clear picture of what you're working with if you inherit several times/deep.
Jul 01 2019
parent Era Scarecrow <rtcvb32 yahoo.com> writes:
On Tuesday, 2 July 2019 at 04:54:07 UTC, Era Scarecrow wrote:
  1) No Virtual functions. All functions are FINAL (replacing)
  2) No polymorphism.
  3) Limited casting (Casting up/back is easier than casting
And looking at my old notes... Might make it where the data size can't change. This crypto stuff used to work in 10/2012, or with DMD 2.060 From the polymorph.d --- auto crypto = PolyCryptoBase.factory("CryptoROT13"); crypto.setString("(Whfg n yvggyr pelcgb grfg)"); crypto.encipher; assert(crypto.data.crypted == "(Just a little crypto test)"); --- Here's a crypto example of the actual structs /** This is not intended as a serious crypto library, just enough to test several factors to ensure the polymorphic nature is being done. As other items need to be tested this crypto will be improved to handle those tests.*/ struct PolyCryptoBase { ///Enum, _prefix and _types don't exist until this calls. mixin(expandTypes("Data", "Poly_", "CryptoBase,CryptoXOR13,CryptoROT13")); /// struct Data { Enum polyMorphType; /// string original; /// char[] crypted; ///mutable copy } Data data; /// ///Seems opCmp opEquals and similar functions go in the poly base. That's livable. ///if not, forward reference to Poly_opCmp mixin PolyMorphicCompare!(PolyCryptoBase); mixin PolyMorphic!("smartmerged.polymorphicstruct_crypto", PolyCryptoBase, Data, "data"); /// } /// struct CryptoBase { mixin PolyMorphicInclude!(PolyCryptoBase); /// ///Seems opCmp opEquals and similar functions go in the poly base. That's livable. ///if not, forward reference to Poly_opCmp int Poly_opCmp(ref const PolyCryptoBase rhs) const { return data.original.length - rhs.data.original.length; } ///if it matches the original string bool isCrypted() property pure safe nothrow{ return data.original != data.crypted; } ///Does basic enciphering, individual letters only static void Poly_cipherChar(ref char c) pure safe nothrow { c ^= 127; } ///set the plaintext (resetting crypted text as well) void setString(string input) property { data.original = input; data.crypted = input.dup; } ///encrypt cipher string (which done twice will decrypt it) void encipher() property { foreach(ref ch; data.crypted) { this.cipherChar(ch); } } } /// struct CryptoXOR13 { mixin PolyMorphicInclude!(PolyCryptoBase);/// /**non matching signatures, but still compatible requires 'static' or const to work, otherwise potential for only calling a different version may apply making some bugs hard to find.*/ static void cipherChar(ref char c) pure safe { c ^= 13; } } Yeah a little long winded, but it did work as a concept... I can share the old full sources if you want.
Jul 01 2019