www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Casting pointers

reply Henning Hasemann <hhasemann web.de> writes:
I have the following code:

import std.stdio;

interface I { void foo(); }

class C : I {
  void foo() { writefln("foo called"); }
}

void main() {
  auto c = new C;
  I* i = cast(I*)(&c);
  (*i).foo();
}

What I'd expect it to do would be to write "foo called".
Instead it prints out "icast.C" with dmd-1.018.

Did I something wrong casting the address of c (which is in fact a
reference to a reference)? If so shouldnt there be a compiler error?

Is there any way to have the address of the Variable c interpreted as
an address to a I-interface at all? I know it works without the address
stuff but I'd really like to use this pointer-to-reference semantic.

Henning

-- 
GPG Public Key:
http://keyserver.ganneff.de:11371/pks/lookup?op=get&search=0xDDD6D36D41911851
Fingerprint: 344F 4072 F038 BB9E B35D  E6AB DDD6 D36D 4191 1851
Jul 07 2007
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Henning Hasemann wrote:
 I have the following code:
 
 import std.stdio;
 
 interface I { void foo(); }
 
 class C : I {
   void foo() { writefln("foo called"); }
 }
 
 void main() {
   auto c = new C;
   I* i = cast(I*)(&c);
   (*i).foo();
 }
 
 What I'd expect it to do would be to write "foo called".
 Instead it prints out "icast.C" with dmd-1.018.
 
 Did I something wrong casting the address of c (which is in fact a
 reference to a reference)? If so shouldnt there be a compiler error?
Yes, you did something wrong. You cast a pointer to an object reference to a pointer to an interface reference and expected it to do something meaningful. Interfaces use different vtables than objects, so the binary representation of the references won't be the same even if they reference the same actual object. (Interface pointers point to the address of the interface vtable pointer in the object instead of to the start of the object (where the class vtable is)). (What's happening in your program: I.foo() probably occupies the same position in the interface vtable as Object.print() does in the class vtable. Since you haven't overridden toString() that then prints the name of the class; I guess your source file is named icast.d?) I don't think this should be a compile error since pointer casting is technically allowed, but an optional warning might be nice. However, pointer-casting is generally a bad idea so the best solution is just to avoid it as much as possible.
 Is there any way to have the address of the Variable c interpreted as
 an address to a I-interface at all? I know it works without the address
 stuff but I'd really like to use this pointer-to-reference semantic.
First cast to a C* (or Object*), then dereference it and cast to I. These'll probably work: --- I i = cast(I) *cast(Object*) &c; // and I i = cast(I) *cast(C*) &c; // though cast(C*) is pretty useless here :) --- (You can't get a pointer to an interface this way unless you store the result of "cast(I) ..." first, since there's no interface reference to point to) By the way, you shouldn't need to use "(*i).whatever"; pointers are dereferenced automatically when members are accessed (except when you need things like (*i).sizeof because those are also valid properties of the pointers themselves).
Jul 07 2007
next sibling parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Frits van Bommel wrote:
 First cast to a C* (or Object*), then dereference it and cast to I.
 These'll probably work:
 ---
     I i = cast(I) *cast(Object*) &c;
 // and
     I i = cast(I) *cast(C*) &c;
 // though cast(C*) is pretty useless here :)
 ---
As you note, cast(C*) is useless, so it boils down to: I i = cast(I) *&c; Which boils down to: I i = cast(I) c; Which boils down to: I i = c; (No cast needed since C inherits from I.) So you've managed to sidestep the whole point, in a way. Although I'm not really sure what the point was, and why one would need a pointer to an interface. Casting to an Object is equivalent, but it's probably slower since it has to do a dynamic cast. Contrast the Object version: L0: push EAX push offset FLAT:_D4asdf1I11__InterfaceZ push offset FLAT:_D4asdf1C7__ClassZ call near ptr __d_newclass add ESP,4 push EAX call near ptr __d_dynamic_cast add ESP,8 mov ECX,[EAX] call dword ptr 4[ECX] With the other versions: L0: push EAX push offset FLAT:_D4asdf1C7__ClassZ call near ptr __d_newclass add ESP,4 test EAX,EAX je L17 lea EAX,8[EAX] jmp short L19 L17: xor EAX,EAX L19: mov ECX,[EAX] call dword ptr 4[ECX] (Retrieved with obj2asm after dmd -O.) -- Remove ".doesnotlike.spam" from the mail address.
Jul 07 2007
parent reply Henning Hasemann <hhasemann web.de> writes:
 So you've managed to sidestep the whole point, in a way. Although I'm
 not really sure what the point was, and why one would need a pointer
 to an interface.
Well the point is a lengthy, boring story (at least *why* I wanted it like this) but I already found a nice and clean way to do it without, so no problem. FYI the construct was something like this (note that I'm too lazy to work out the correct casting again, I'll just show the basic idea): class MainLoop { IRenderable*[] renderables; // render the renderables in a method } class UserInterface : IRenderable { } class Scene : IRenderable { } UserInterface globalUserInterface; Scene currentScene; MainLoop mainLoop; mainLoop.renderables ~= &globalUserInterface; mainLoop.renderables ~= &currentScene; // some time later: currentScene = someOtherScene; // and/or: globalUserInterface = specialUserInterfaceForMiniGame; MainLoop was not able to import the userInterface and Scene modules so it had no direct access to these variables. But I could break some import cycles so that is no problem anymore and mainLoop now directly refers to them (so not even the interface would not be necessary for that part) Henning -- GPG Public Key: http://keyserver.ganneff.de:11371/pks/lookup?op=get&search=0xDDD6D36D41911851 Fingerprint: 344F 4072 F038 BB9E B35D E6AB DDD6 D36D 4191 1851
Jul 07 2007
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Henning Hasemann wrote:
 FYI the construct was something like this (note that I'm too lazy to
 work out the correct casting again, I'll just show the basic idea):
 
 class MainLoop {
   IRenderable*[] renderables;
   // render the renderables in a method
 }
 
 class UserInterface : IRenderable {
 }
 
 class Scene : IRenderable {
 }
 
 UserInterface globalUserInterface;
 Scene currentScene;
 
 MainLoop mainLoop;
 mainLoop.renderables ~= &globalUserInterface;
 mainLoop.renderables ~= &currentScene;
 
 // some time later:
 currentScene = someOtherScene;
 // and/or:
 globalUserInterface = specialUserInterfaceForMiniGame;
  
The thing about D is that interfaces and classes are already reference types. IRenderable* here is equivalent to IRenderable** in C++. For your code above, using just IRenderable would have worked just fine. -- Remove ".doesnotlike.spam" from the mail address.
Jul 07 2007
parent Henning Hasemann <hhasemann web.de> writes:
 The thing about D is that interfaces and classes are already
 reference types.
I know, I tried already to let this shine through by writing "... the address of c (which is in fact a reference to a reference)..." in order to avoid this misunderstanding :) What I wanted was to be able to let the global variables point to a different object later (rebinding them or whatever is the correct wording), but having the main loop thingy automatically reference the newly assigned object *without* having to import the modules of the globals type in the main loop module. (main loop should only know about the interface). But, again, I already refactored this so such a confusing technique is not necessary anymore. Henning -- GPG Public Key: http://keyserver.ganneff.de:11371/pks/lookup?op=get&search=0xDDD6D36D41911851 Fingerprint: 344F 4072 F038 BB9E B35D E6AB DDD6 D36D 4191 1851
Jul 07 2007
prev sibling parent Henning Hasemann <hhasemann web.de> writes:
Thanks for the fast and enlightning answer :)

Henning

-- 
GPG Public Key:
http://keyserver.ganneff.de:11371/pks/lookup?op=get&search=0xDDD6D36D41911851
Fingerprint: 344F 4072 F038 BB9E B35D  E6AB DDD6 D36D 4191 1851
Jul 07 2007