June 13, 2019
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.
June 12, 2019
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.

June 12, 2019
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?
June 12, 2019
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
June 12, 2019
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.
June 13, 2019
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.)
June 12, 2019
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
June 13, 2019
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.
June 13, 2019
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 deserve their own threads: #1 struct inheritance/extension; #2 bit size (related to Manu's application but in general independent of #1); and #3 void initialization of value types 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."
June 13, 2019
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?