Jump to page: 1 2
Thread overview
May 01

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:

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:

auto b = new B;
auto p = cast(void*) b;
auto c = cast(AI) p; // AI with corrupt data

c.doSomething(); // error

But this works:

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?

May 01

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.

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.

May 01

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

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.

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

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

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
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

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 04

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:

>

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.

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

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:

>

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.

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.)

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.

« First   ‹ Prev
1 2