November 25, 2009

Bill Baxter wrote:
> On Tue, Nov 24, 2009 at 3:09 PM, Saaa <empty@needmail.com> wrote:
> 
>>>> I wanted to do something like this:
>>>>
>>>> class C : I {};
>>>> struct S : I {};
>>>> S s;
>>>> I[] i =[new C(), s ];
>>> Yeh, that's never going to work because that's acting as a dynamic polymorphic interaface.  Referring polymorphically to a struct like that pretty much makes it not a struct anymore, and requires having the hidden pointer to a vtable that was mentioned.  That's what classes are for.
>> Why is a hidden pointer necessary? (Just curious :)
>>
>> My simplistic view was like this:
>> i[1] would just hold the location of s and s would be checked to have
>> all it needs to be an I.
> 
> I think it could be done with a different implementation of interfaces
> from the one D uses, one based on "fat pointers".
> With that design an I referring to an S would be a "fat pointer", one
> pointer pointing to the S and one pointing to S's table of function
> pointers (vtable) for the I interface.
> 
> That's not how D does it AFAIR, but I don't actually recall how D does it.
> 
> --bb

(This is all off the top of my head.)

In D, interfaces are pointers to the vtable which implements the interface for a particular class.  In order to actually get the "this" reference, D stores a pointer to the class' InterfaceInfo (or something) for that interface in the first slot of the vtable.

This InterfaceInfo indicates how far from the start of an instance the pointer to the vtable is contained.  To get "this", you take the pointer to the interface vtable and subtract this offset.

This is why interfaces cannot be implemented by structs in D: it would require structs to grow magical hidden fields, which is explicitly against the stated purpose of structs: plain old data.

Even then, there's a worse problem.  All interfaces can be cast to Object, and then upcast to any valid class.  This is done via the use of the first slot of the object's vtable, which contains the ClassInfo.

But if you allow structs as interfaces, you're suddenly in the position where you might not actually have an object at all.  If you tried to cast a struct to an Object, it might not actually fail; if you're lucky, you'll get a segfault.

The only solution there is to give structs a vtable.  At which point, congratulations, you've just re-invented classes.

To allow structs to implement interfaces would require redesigning how interfaces are actually implemented.  You'd probably have to also redesign RTTI as well, object casting, etc.
November 25, 2009
On Tue, Nov 24, 2009 at 4:38 PM, Daniel Keep <daniel.keep.lists@gmail.com> wrote:
>
> (This is all off the top of my head.)
>

<nice explanation snipped -- thanks>

> Even then, there's a worse problem.  All interfaces can be cast to Object, and then upcast to any valid class.  This is done via the use of the first slot of the object's vtable, which contains the ClassInfo.

Well, except for those dreaded "IUnknown" COM interfaces.

> But if you allow structs as interfaces, you're suddenly in the position where you might not actually have an object at all.  If you tried to cast a struct to an Object, it might not actually fail; if you're lucky, you'll get a segfault.

Same as with IUnknown interfaces.

> The only solution there is to give structs a vtable.  At which point, congratulations, you've just re-invented classes.
>
> To allow structs to implement interfaces would require redesigning how interfaces are actually implemented.  You'd probably have to also redesign RTTI as well, object casting, etc.

But with good enough introspection, it should be possible to make automatic class wrappers for structs that implement an interface by forwarding to the struct.

That's probably mostly doable now, though probably with a frightening assortment method signature string parsing and mixins.

--bb
November 25, 2009
Bill Baxter wrote:
<daniel.keep.lists@gmail.com> wrote:
>
> (This is all off the top of my head.)
>

<nice explanation snipped -- thanks>

< snipped --bb >

Thanks for all the explanations!


November 25, 2009
Bill Baxter:

> The good thing is that since most of the machinery is there, the actual compiler changes required would mostly be just rewrites of new syntax in terms of existing functionality.

I agree, this looks like something that can be added to D even after D2 comes out of alpha.
But Andrei warns us that here it's easy to overdo the design. So I think that keeping usability low (= keeping things handmade and low-tech) can be better than a Byzantine and limited design.


> In particular there's not a good way for the compiler to give good error messages about why a concept is not satisfied by a particular type.

Time ago I have asked for a "small" compiler feature: when a function/class template creates a compile-time error (because some of the code inside it is not compatible with the specific type given to the template) I'd like the D compiler to act as GCC, telling me not just where there is the error inside the template, but also and *before* that error message to show me the line of where the template is instantiated. This is another small feature that can be added after D2 "finalization".

Bye,
bearophile
November 25, 2009
Daniel Keep:

> This is why interfaces cannot be implemented by structs in D: it would require structs to grow magical hidden fields, which is explicitly against the stated purpose of structs: plain old data.

In D2 if you define a struct inside a function it (in theory) grows an hidden field, see Nested Structs here: http://www.digitalmars.com/d/2.0/struct.html

I have written in theory because in practice the size is the same :-) And the following code doesn't compile, static structs are not implemented yet it seems:

void foo() {
    static struct Inner {
        int x;
    }
}

Walter needs an automatic way to test code snippets inside the docs...

Bye,
bearophile
November 25, 2009
bearophile wrote:
> Bill Baxter:
> 
>> The good thing is that since most of the machinery is there, the
>> actual compiler changes required would mostly be just rewrites of new
>> syntax in terms of existing functionality.
> 
> I agree, this looks like something that can be added to D even after D2 comes out of alpha.
> But Andrei warns us that here it's easy to overdo the design. So I think that keeping usability low (= keeping things handmade and low-tech) can be better than a Byzantine and limited design.
> 
> 
>> In particular there's not a good way for the compiler to give good
>> error messages about why a concept is not satisfied by a particular
>> type.
> 
> Time ago I have asked for a "small" compiler feature: when a function/class template creates a compile-time error (because some of the code inside it is not compatible with the specific type given to the template) I'd like the D compiler to act as GCC, telling me not just where there is the error inside the template, but also and *before* that error message to show me the line of where the template is instantiated. This is another small feature that can be added after D2 "finalization".

That's been requested many times. I posted a patch to Walter to do exactly that. It was beautiful. It detected recursive template expansions, and gave really nice error messages. Silently rejected.
Sigh.
November 25, 2009
Don wrote:
> bearophile wrote:
>> Bill Baxter:
>>
>>> The good thing is that since most of the machinery is there, the
>>> actual compiler changes required would mostly be just rewrites of new
>>> syntax in terms of existing functionality.
>>
>> I agree, this looks like something that can be added to D even after D2 comes out of alpha.
>> But Andrei warns us that here it's easy to overdo the design. So I think that keeping usability low (= keeping things handmade and low-tech) can be better than a Byzantine and limited design.
>>
>>
>>> In particular there's not a good way for the compiler to give good
>>> error messages about why a concept is not satisfied by a particular
>>> type.
>>
>> Time ago I have asked for a "small" compiler feature: when a function/class template creates a compile-time error (because some of the code inside it is not compatible with the specific type given to the template) I'd like the D compiler to act as GCC, telling me not just where there is the error inside the template, but also and *before* that error message to show me the line of where the template is instantiated. This is another small feature that can be added after D2 "finalization".
> 
> That's been requested many times. I posted a patch to Walter to do exactly that. It was beautiful. It detected recursive template expansions, and gave really nice error messages. Silently rejected.
> Sigh.


What's the bugzilla number? Should be upvoted.

-Lars
November 25, 2009
"Saaa" <empty@needmail.com> wrote:

> struct S : Pos {}
> Why is this not possible?
> 
> 
> 

Why do you want to use a struct for that, rather than a class?

November 25, 2009
Don:
> That's been requested many times. I posted a patch to Walter to do exactly that. It was beautiful. It detected recursive template expansions, and gave really nice error messages. Silently rejected. Sigh.

If your patch is well done, works with LDC too, I see no reason to refuse this feature even for D1, it doesn't change the language and just makes debugging simpler. So let's not surrender yet. How much time ago Walter has refused this patch? Lately Walter is more receptive for your patches. I can create a new thread about this in the main D NG. If 8+ persons say they want this patch, and it works correctly, then Walter can change his mind.

Bye,
bearophile
November 25, 2009
Lars T. Kyllingstad:

> What's the bugzilla number? Should be upvoted.

I think he refers to this one (searching something in Bugzilla seems a good way to spend a morning): http://d.puremagic.com/issues/show_bug.cgi?id=2816

In the comment 9, instead of:
bug.d(2): Error: static assert  (0) is false
bug.d(9):        instantiatied from here: bar!()
bug.d(14):        100 recursive instantiations from here: foo!(196)
bug.d(19):        253 recursive instantiations from here: baz!(300)

Someone may prefer them listed in inverted order:
bug.d(19): ...
bug.d(14): ...
bug.d(9): ...
bug.d(2): ...

Bye,
bearophile