www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - why can't structs implement interfaces?

reply "Saaa" <empty needmail.com> writes:
struct S : Pos {}
Why is this not possible? 
Nov 24 2009
next sibling parent reply Moritz Warning <moritzwarning web.de> writes:
On Tue, 24 Nov 2009 21:55:02 +0100, Saaa wrote:

 struct S : Pos {}
 Why is this not possible?
The struct would need an additional pointer for a dispatch table in case you want to have class semantics. If you only what a contract that certain functions are implemented, then it just need to be implemented in the compiler frontend.
Nov 24 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Moritz Warning:

 If you only what a contract that certain functions are implemented,
 then it just need to be implemented in the compiler frontend.
In the meantime this can be done with a template mixin, where the template statically asserts the presence of the functions/fields you want. Bye, bearophile
Nov 24 2009
next sibling parent reply "Saaa" <empty needmail.com> writes:
bearophile wrote:
 Moritz Warning:

 If you only what a contract that certain functions are implemented,
 then it just need to be implemented in the compiler frontend.
In the meantime this can be done with a template mixin, where the template statically asserts the presence of the functions/fields you want. Bye, bearophile
I wanted to do something like this: class C : I {}; struct S : I {}; S s; I[] i =[new C(), s ];
Nov 24 2009
parent reply Bill Baxter <wbaxter gmail.com> writes:
On Tue, Nov 24, 2009 at 2:49 PM, Saaa <empty needmail.com> wrote:
 bearophile wrote:
 Moritz Warning:

 If you only what a contract that certain functions are implemented,
 then it just need to be implemented in the compiler frontend.
In the meantime this can be done with a template mixin, where the template statically asserts the presence of the functions/fields you want. Bye, bearophile
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. In D2 you can use "alias this" inside a class to forward things to the struct, though. Something like this: class ClassWrapper(S) : I { S _impl; alias _impl this; } But I somehow doubt DMD will consider methods handled by S as being an implementation of the interface. So you'll need explicit forwarding. --bb
Nov 24 2009
next sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
Bill Baxter wrote:

 On Tue, Nov 24, 2009 at 2:49 PM, Saaa <empty needmail.com> wrote:
 bearophile wrote:
 Moritz Warning:

 If you only what a contract that certain functions are implemented,
 then it just need to be implemented in the compiler frontend.
In the meantime this can be done with a template mixin, where the template statically asserts the presence of the functions/fields you want. Bye, bearophile
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. In D2 you can use "alias this" inside a class to forward things to the struct, though. Something like this: class ClassWrapper(S) : I { S _impl; alias _impl this; } But I somehow doubt DMD will consider methods handled by S as being an implementation of the interface. So you'll need explicit forwarding. --bb
I can confirm that it is not possible with alias this. Somewhat related, template mixins can add virtual methods: template T() { /*implementation of I*/ } class C : I { mixin T!(); } // adds methods in T!() as virtual methods
Nov 24 2009
prev sibling parent reply "Saaa" <empty needmail.com> writes:
Bill Baxter wrote:
 On Tue, Nov 24, 2009 at 2:49 PM, Saaa <empty needmail.com> wrote:
 bearophile wrote:
 Moritz Warning:

 If you only what a contract that certain functions are implemented,
 then it just need to be implemented in the compiler frontend.
In the meantime this can be done with a template mixin, where the template statically asserts the presence of the functions/fields you want. Bye, bearophile
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.
 In D2 you can use "alias this" inside a class to forward things to the
 struct, though. Something like this:

 class ClassWrapper(S) : I {
   S _impl;
   alias _impl this;
 }

 But I somehow doubt DMD will consider methods handled by S as being an
 implementation of the interface.
 So you'll need explicit forwarding.

 --bb 
Nov 24 2009
parent reply Bill Baxter <wbaxter gmail.com> writes:
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 =3D[new C(), s ];
Yeh, that's never going to work because that's acting as a dynamic polymorphic interaface. =A0Referring 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. =A0That'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
Nov 24 2009
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
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.
Nov 24 2009
next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
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. =A0All interfaces can be cast to
 Object, and then upcast to any valid class. =A0This is done via the use o=
f
 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. =A0If 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. =A0At which point,
 congratulations, you've just re-invented classes.

 To allow structs to implement interfaces would require redesigning how
 interfaces are actually implemented. =A0You'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
Nov 24 2009
parent "Saaa" <empty needmail.com> writes:
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!
Nov 24 2009
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
Nov 24 2009
prev sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Tue, Nov 24, 2009 at 2:36 PM, bearophile <bearophileHUGS lycos.com> wrote:
 Moritz Warning:

 If you only what a contract that certain functions are implemented,
 then it just need to be implemented in the compiler frontend.
In the meantime this can be done with a template mixin, where the template statically asserts the presence of the functions/fields you want.
I really think some kind of static interface (concept) support is going to be necessary eventually. In terms of functionality, static asserts and "isInterfaceSupported()" methods are OK. But the usability is not great. 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. I tried to come up with a way to do that given access to compiler error messages, but the result looks rather like attempts to do runtime inheritance in C. 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. --bb
Nov 24 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
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
Nov 24 2009
parent reply Don <nospam nospam.com> writes:
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.
Nov 25 2009
next sibling parent reply "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
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
Nov 25 2009
parent bearophile <bearophileHUGS lycos.com> writes:
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
Nov 25 2009
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
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
Nov 25 2009
parent reply Don <nospam nospam.com> writes:
bearophile wrote:
 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.
Please don't do that just yet. It's something that can wait until the D2 stuff is finished. It can be added at any time. Some of the other things are urgent.
Nov 25 2009
parent bearophile <bearophileHUGS lycos.com> writes:
Don:
 Please don't do that just yet. It's something that can wait until the D2 
 stuff is finished. It can be added at any time. Some of the other things 
 are urgent.
OK :-) You are right, as usual. Bye, bearophile
Nov 25 2009
prev sibling next sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
Saaa wrote:

 struct S : Pos {}
 Why is this not possible?
 
 
Because structs are meant to be value types and thus do not implement dynamic polymorphism, which is what interfaces are used for. It is not necessary though, classes in C++ are almost the same as structs for example, but there are problems with that design.
Nov 24 2009
prev sibling next sibling parent Rory McGuire <rjmcguire gmail.com> writes:
"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?
Nov 25 2009
prev sibling parent wakko <dm liquidstate.eu> writes:
Saaa a écrit :
 struct S : Pos {}
 Why is this not possible? 
 
It's not and structures have no vtable... fortunately. But, could it be a good idea to use the "inheritance operator" to do some kind of static inheritance like we do with mixins or to force implementation at compile-time ? 1. inheriting from an interfaces would force function implementation at compile time: interface IFoo { void foo(); } // Would not compile // "Struct S static interface function IFoo.foo isn't implemented" struct S : IFoo {} // Would compile struct S : IFoo { void foo() {} } 2. inheriting from a structure would do some kind a mixin. struct S1 { void foo(); static void bar(); } struct S2 : S1 {} S1.bar(); S1 a; a.foo(); a.bar(); 3. class and structs could share interfaces for compile time function name resolution. I think this already work using untyped template arguments. interface IFoo { void foo(); } class C : IFoo // dynamic inheritance { void foo() { } } struct S : IFoo // static inheritance { void foo() { } } class Bar(IFoo FOO) { void bar(FOO thing) { thing.foo(); } } 4. casting could be forbidden OR : struct A { int a; } struct B : A {} // B implements "int a" as in 2 A a; B b = a; // would do something like b.a = a.a B b; A a = b; // would do something like a.a = b.a -- AF
Nov 25 2009