digitalmars.D.learn - Should this always work?
- frame (44/44) Apr 30 2021 I always thought as long as an object implements an interface, it
- Mike Parker (21/26) Apr 30 2021 No. An interface is like a pointer to a pointer. So to get to the
- frame (5/8) May 01 2021 Thanks for clarification.
- Mike Parker (29/32) May 01 2021 I wouldn't expect there to be any consideration of vtables
- frame (5/9) May 01 2021 I see, this can be dangerous - yet, the only way in my
- Mike Parker (5/7) May 01 2021 No instance is being created by the cast. The instance already
- frame (8/21) May 01 2021 Yes, I know. In fact, this was a stupid question. Not sure what I
- Q. Schroll (4/9) May 03 2021 Can you elaborate on this one? I don't really get it. Is an
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (15/25) May 04 2021 Off the top of my head the object layout is something like this:
- Steven Schveighoffer (10/40) May 04 2021 Yes, this is exactly how it is. See the ABI document:
- Mike Parker (6/12) May 04 2021 So I have no deep knowledge of the implementation details. I only
- Steven Schveighoffer (17/76) May 01 2021 An interface cast involves a thunk (constant pointer adjustment) to get
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (3/5) May 04 2021 Yes, but it isn't a https://en.wikipedia.org/wiki/Thunk ?
- Paul Backus (3/8) May 04 2021 The article literally gives this exact use-case as an example:
- Steven Schveighoffer (5/15) May 04 2021 Yeah, I wasn't aware of the more general usage, I thought it was always
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (5/8) May 04 2021 My understanding is that a thunk is the code object that fetches
- Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= (14/23) May 04 2021 I guess in D terms it can be best explained as a compiler
I always thought as long as an object implements an interface, it should be able to cast it from a void* if it really points to a supporting object. I have the similar structure: ```d interface AI { string doSomething(); } template S() { void foo() { } } abstract class A : AI { string doSomething() { return "Hello, World"; } } class B : A { mixin S; void other() { } } auto b = new B; auto p = cast(void*) b; auto c = cast(AI) p; c.doSomething(); ``` But in my code with the real object, this generates a RangeError, AcccesError, memory garbage: ```d auto b = new B; auto p = cast(void*) b; auto c = cast(AI) p; // AI with corrupt data c.doSomething(); // error ``` But this works: ```d auto b = new B; auto p = cast(void*) b; auto c = cast(A) p; // A with correct data c.doSomething(); // no error ``` If the runtime could not successfully cast it to AI, it should return null. Am I wrong here?
Apr 30 2021
On Saturday, 1 May 2021 at 04:55:10 UTC, frame wrote:I always thought as long as an object implements an interface, it should be able to cast it from a void* if it really points to a supporting object.No. An interface is like a pointer to a pointer. So to get to the class, you have to go one more level of indirection. ```d import std.stdio; interface I { void doIt(); } class A : I { void doIt() { writeln("Doing it"); } } void main() { I i = new A; void** pi = cast(void**)i; A a = cast(A)(*pi); a.doIt(); } ```If the runtime could not successfully cast it to AI, it should return null. Am I wrong here?That only works when you're casting one class/interface reference to another. It doesn't work when you're casting raw pointers.
Apr 30 2021
On Saturday, 1 May 2021 at 06:17:36 UTC, Mike Parker wrote:That only works when you're casting one class/interface reference to another. It doesn't > > work when you're casting raw pointers.Thanks for clarification. In case of a void* to abstract class casting, I assume the compiler just inserts the right vtable, so this cast is unproblematic - or is there also a pitfall?
May 01 2021
On Saturday, 1 May 2021 at 08:03:45 UTC, frame wrote:In case of a void* to abstract class casting, I assume the compiler just inserts the right vtable, so this cast is unproblematic - or is there also a pitfall?I wouldn't expect there to be any consideration of vtables whatsoever. The compiler just reinterprets the pointer as a class reference, whether you're casting it to the original class or a superclass type. I mean, all of the functions available to the class are already in the vtable. There's no need to insert anything. Whether you're getting at a class instance through a subclass (C) reference or a superclass (A) reference, abstract or not, it's still only a single instance located at a single address. ```d import std.stdio; abstract class A { void doIt(); } class C : A { override void doIt() { writeln("Doing it"); } } void main() { C c = new C; void* pc = cast(void*)c; A a = cast(A)pc; a.doIt(); } ``` So the pitfall is the same: when you're casting class references to and from pointers, it's up to you to ensure that the type you cast to is appropriate. You get no help from the compiler here.
May 01 2021
On Saturday, 1 May 2021 at 10:08:17 UTC, Mike Parker wrote:So the pitfall is the same: when you're casting class references to and from pointers, it's up to you to ensure that the type you cast to is appropriate. You get no help from the compiler here.I see, this can be dangerous - yet, the only way in my application (DLL problem). btw: why is it even allowed to create an instance via cast as an abstract thing?
May 01 2021
On Saturday, 1 May 2021 at 15:21:28 UTC, frame wrote:btw: why is it even allowed to create an instance via cast as an abstract thing?No instance is being created by the cast. The instance already exists. The cast just allows you to treat the pointer to the instance as a specific type. It’s no different than casting from C to A really. You just have the void* as an intermediary.
May 01 2021
On Saturday, 1 May 2021 at 15:52:52 UTC, Mike Parker wrote:No instance is being created by the cast. The instance already exists. The cast just allows you to treat the pointer to the instance as a specific type. It’s no different than casting from C to A really. You just have the void* as an intermediary.Yes, I know. In fact, this was a stupid question. Not sure what I was thinking off. I sometimes mix up languages in my head and came to weird conclusions :P On Saturday, 1 May 2021 at 16:06:05 UTC, Steven Schveighoffer wrote:An interface cast involves a thunk (constant pointer adjustment) to get to the interface/object. The reason is because a class with interfaces stores interface vtable pointers inside the object, and your interface reference points at that. You can see when you cast between Object (concrete) type and Interface type, the pointer value changes. So this will not work. It *does* work for base classes, because the class vtable pointer is stored at same point, and casting around class references does not involve a thunk.This was helpful to understand the ABI chapter better. Thanks, guys!
May 01 2021
On Saturday, 1 May 2021 at 06:17:36 UTC, Mike Parker wrote:On Saturday, 1 May 2021 at 04:55:10 UTC, frame wrote:Can you elaborate on this one? I don't really get it. Is an object handle also like a pointer to a pointer? (I feel like I can learn something here.)I always thought as long as an object implements an interface, it should be able to cast it from a void* if it really points to a supporting object.No. An interface is like a pointer to a pointer.
May 03 2021
On Tuesday, 4 May 2021 at 01:20:15 UTC, Q. Schroll wrote:On Saturday, 1 May 2021 at 06:17:36 UTC, Mike Parker wrote:Off the top of my head the object layout is something like this: { pointer to vtable0 for the class; monitor mutex stuff; pointer to interface 1 vtable1; pointer to interface 2 vtable2; ... pointer to interface N vtableN; object data 1; object data 2; ... } A pointer to interface 1 is a pointer to the pointer to vtable1 in the object above.On Saturday, 1 May 2021 at 04:55:10 UTC, frame wrote:Can you elaborate on this one? I don't really get it. Is an object handle also like a pointer to a pointer? (I feel like I can learn something here.)I always thought as long as an object implements an interface, it should be able to cast it from a void* if it really points to a supporting object.No. An interface is like a pointer to a pointer.
May 04 2021
On 5/4/21 6:03 AM, Ola Fosheim Grøstad wrote:On Tuesday, 4 May 2021 at 01:20:15 UTC, Q. Schroll wrote:Yes, this is exactly how it is. See the ABI document: https://dlang.org/spec/abi.html#classes So a class reference is *also* a pointer to a pointer (to the vtable), and a interface reference is the same. However, the slight difference is, the pointer to the interface doesn't allow useful mechanisms until you apply the offset (necessitating a double indirection), whereas a class pointer (combined with compile-time type knowledge) the compiler can access member fields of the class. -SteveOn Saturday, 1 May 2021 at 06:17:36 UTC, Mike Parker wrote:Off the top of my head the object layout is something like this: { pointer to vtable0 for the class; monitor mutex stuff; pointer to interface 1 vtable1; pointer to interface 2 vtable2; ... pointer to interface N vtableN; object data 1; object data 2; ... } A pointer to interface 1 is a pointer to the pointer to vtable1 in the object above.On Saturday, 1 May 2021 at 04:55:10 UTC, frame wrote:Can you elaborate on this one? I don't really get it. Is an object handle also like a pointer to a pointer? (I feel like I can learn something here.)I always thought as long as an object implements an interface, it should be able to cast it from a void* if it really points to a supporting object.No. An interface is like a pointer to a pointer.
May 04 2021
On Tuesday, 4 May 2021 at 01:20:15 UTC, Q. Schroll wrote:On Saturday, 1 May 2021 at 06:17:36 UTC, Mike Parker wrote:So I have no deep knowledge of the implementation details. I only know that a class reference is a pointer under the hood, so it can be cast to `void*` and back, and that an interface reference has an extra level of indirection, so it can go to `void**` and back.No. An interface is like a pointer to a pointer.Can you elaborate on this one? I don't really get it. Is an object handle also like a pointer to a pointer? (I feel like I can learn something here.)
May 04 2021
On 5/1/21 12:55 AM, frame wrote:I always thought as long as an object implements an interface, it should be able to cast it from a void* if it really points to a supporting object. I have the similar structure: ```d interface AI { string doSomething(); } template S() { void foo() { } } abstract class A : AI { string doSomething() { return "Hello, World"; } } class B : A { mixin S; void other() { } } auto b = new B; auto p = cast(void*) b; auto c = cast(AI) p; c.doSomething(); ``` But in my code with the real object, this generates a RangeError, AcccesError, memory garbage: ```d auto b = new B; auto p = cast(void*) b; auto c = cast(AI) p; // AI with corrupt data c.doSomething(); // error ``` But this works: ```d auto b = new B; auto p = cast(void*) b; auto c = cast(A) p; // A with correct data c.doSomething(); // no error ``` If the runtime could not successfully cast it to AI, it should return null. Am I wrong here?An interface cast involves a thunk (constant pointer adjustment) to get to the interface/object. The reason is because a class with interfaces stores interface vtable pointers inside the object, and your interface reference points at that. You can see when you cast between Object (concrete) type and Interface type, the pointer value changes. So this will not work. It *does* work for base classes, because the class vtable pointer is stored at same point, and casting around class references does not involve a thunk. If you want this to work, you have to know whether the void* pointer is pointing at an object, or an interface. If you know it's pointing at an object, you can get to the interface via: auto c = cast(AI)cast(Object)p; Which will perform the appropriate thunks. If you know it's a pointer to the AI interface directly, you can just cast it directly. -Steve
May 01 2021
On Saturday, 1 May 2021 at 16:06:05 UTC, Steven Schveighoffer wrote:An interface cast involves a thunk (constant pointer adjustment) to get to the interface/objectYes, but it isn't a https://en.wikipedia.org/wiki/Thunk ?
May 04 2021
On Tuesday, 4 May 2021 at 10:21:42 UTC, Ola Fosheim Grøstad wrote:On Saturday, 1 May 2021 at 16:06:05 UTC, Steven Schveighoffer wrote:The article literally gives this exact use-case as an example: https://en.wikipedia.org/wiki/Thunk#Object-oriented_programmingAn interface cast involves a thunk (constant pointer adjustment) to get to the interface/objectYes, but it isn't a https://en.wikipedia.org/wiki/Thunk ?
May 04 2021
On 5/4/21 9:21 AM, Paul Backus wrote:On Tuesday, 4 May 2021 at 10:21:42 UTC, Ola Fosheim Grøstad wrote:Yeah, I wasn't aware of the more general usage, I thought it was always a pointer adjustment. But I also am not steeped in the terminology, just parroting what I've heard. -SteveOn Saturday, 1 May 2021 at 16:06:05 UTC, Steven Schveighoffer wrote:The article literally gives this exact use-case as an example: https://en.wikipedia.org/wiki/Thunk#Object-oriented_programmingAn interface cast involves a thunk (constant pointer adjustment) to get to the interface/objectYes, but it isn't a https://en.wikipedia.org/wiki/Thunk ?
May 04 2021
On Tuesday, 4 May 2021 at 13:58:59 UTC, Steven Schveighoffer wrote:Yeah, I wasn't aware of the more general usage, I thought it was always a pointer adjustment. But I also am not steeped in the terminology, just parroting what I've heard.My understanding is that a thunk is the code object that fetches the value for you, in this case there might not be a thunk involved as the interface code can just apply a fixed offset?
May 04 2021
On Tuesday, 4 May 2021 at 14:18:30 UTC, Ola Fosheim Grøstad wrote:On Tuesday, 4 May 2021 at 13:58:59 UTC, Steven Schveighoffer wrote:I guess in D terms it can be best explained as a compiler internal "delegate". Let's take the example of getting the this-pointer: Say, you want to write generic code gen, then you can let that code gen take a "delegate" that computes a this-pointer rather than writing many different code gens for different layouts. Then you trust the optimizer to get rid of it, or keep it if need be. That delegate would be a thunk. It may or may not exist at run-time based on the optimizer/language semantics. You could also do something similar with an offset or switch statement. The point is, the code gen have no idea of how to obtain the this-pointer, the thunk does. It is a separate external code piece that obtains the value of the this-pointer.Yeah, I wasn't aware of the more general usage, I thought it was always a pointer adjustment. But I also am not steeped in the terminology, just parroting what I've heard.My understanding is that a thunk is the code object that fetches the value for you, in this case there might not be a thunk involved as the interface code can just apply a fixed offset?
May 04 2021