www.digitalmars.com         C & C++   DMDScript  

D - [BUG] casting Interface to class-instance generates bogus code

reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
I have a concrete-class that implements a generic interface. On occasion,
said interface needs to be upcast to the concrete-class to access
specialized functionality. Check out the cast(SocketConduit) code below ...
it explicitly sets the lValue to zero.  Good Grief ...

112:          char[] getRemoteAddress (IConduit conduit)
00403290   enter       0Ch,0
00403294   mov         dword ptr [ebp-0Ch],eax
113:          {
114:                  SocketConduit socket = cast(SocketConduit) conduit;
00403297   xor         eax,eax
00403299   mov         dword ptr [socket],eax
Apr 04 2004
next sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
On a hunch, I thought I'd try the old (void *) trick.

As you can see below, it actually persuaded the compiler into doing the
right thing. Oh well; onto the next showstopper.

112:          char[] getRemoteAddress (IConduit conduit)
00403290   enter       0Ch,0
00403294   mov         dword ptr [ebp-0Ch],eax
113:          {
114:                  SocketConduit socket = cast(SocketConduit) cast(void
*) conduit;
00403297   mov         eax,dword ptr [conduit]
0040329A   mov         dword ptr [socket],eax
Apr 04 2004
parent reply Ant <duitoolkit yahoo.ca> writes:
On Sun, 04 Apr 2004 19:25:18 -0800, Kris wrote:

 On a hunch, I thought I'd try the old (void *) trick.
 
 114:                  SocketConduit socket = cast(SocketConduit) cast(void
 *) conduit;

great. I had the same problem before and my work around was... so complex that I even don't remember. Ant
Apr 04 2004
parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
Oops; I'm afraid I jumped the gun on that one. The (void *) cast drops some
of the vTable offsets (or something), so it end's up calling the wrong
place. I'm not sure if that's better or worse than before :-)

I was just going through the code to see if I could avoid the interface
altogether, and I'm afraid that would make the whole system specific to
SocketConduit only. Hence that is simply not an option.

If you can recall what you did to resolve your issue? I'd really appreciate
any help Ant.

Cheers,

- Kris

 Ant wrote:
 great. I had the same problem before and my work
 around was... so complex that I even don't remember.

 Ant

Apr 04 2004
parent Ant <duitoolkit yahoo.ca> writes:
On Sun, 04 Apr 2004 19:48:27 -0800, Kris wrote:

 Oops; I'm afraid I jumped the gun on that one. The (void *) cast drops some
 of the vTable offsets (or something), so it end's up calling the wrong
 place. I'm not sure if that's better or worse than before :-)
 
 I was just going through the code to see if I could avoid the interface
 altogether, and I'm afraid that would make the whole system specific to
 SocketConduit only. Hence that is simply not an option.
 
 If you can recall what you did to resolve your issue? I'd really appreciate
 any help Ant.
 
 Cheers,
 
 - Kris

Sorry, I don't think I can help, I really don't remember but I think I redesign something and I'm avoiding it since. I believe this is covered on Burton Radons interface and inheritance bug report let's hope Walter is looking at that. Ant
Apr 04 2004
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
The problem here is that all an interface is, when implemented, is a pointer
to a vtbl[]. There's no way the runtime can tell where it came from, so
there is no way to upcast it.

The way to deal with this is to add a member function to the interface that
is equivalent to the 'ClassFactory' function in COM programming. Then, in
the implementation of that function, which does know the object type
enclosing the interface, the cast can be done.

interface IConduit
{
    SocketConduit isSocketConduit();
}

class SocketConduit : IConduit
{
    SocketConduit isSocketConduit() { return this; }
}

char[] getRemoteAddress(IConduit conduit)
{
    SocketConduit socket = conduit.isSocketConduit();
}


"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:c4qi35$24f8$1 digitaldaemon.com...
 I have a concrete-class that implements a generic interface. On occasion,
 said interface needs to be upcast to the concrete-class to access
 specialized functionality. Check out the cast(SocketConduit) code below

 it explicitly sets the lValue to zero.  Good Grief ...

 112:          char[] getRemoteAddress (IConduit conduit)
 00403290   enter       0Ch,0
 00403294   mov         dword ptr [ebp-0Ch],eax
 113:          {
 114:                  SocketConduit socket = cast(SocketConduit) conduit;
 00403297   xor         eax,eax
 00403299   mov         dword ptr [socket],eax

Apr 05 2004
next sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
Thank-you Walter. I truly appreciate your help on this.

BTW, is this the expected long-term solution? If so (and I don't think it
should be <g>) the compiler really needs to toss an error, rather than
emitting grossly unjust access-violators ...

- Kris


"Walter" <walter digitalmars.com> wrote in message
news:c4sk0f$2eon$1 digitaldaemon.com...
 The problem here is that all an interface is, when implemented, is a

 to a vtbl[]. There's no way the runtime can tell where it came from, so
 there is no way to upcast it.

 The way to deal with this is to add a member function to the interface

 is equivalent to the 'ClassFactory' function in COM programming. Then, in
 the implementation of that function, which does know the object type
 enclosing the interface, the cast can be done.

Apr 05 2004
parent "Walter" <walter digitalmars.com> writes:
"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:c4ska9$2f70$1 digitaldaemon.com...
 Thank-you Walter. I truly appreciate your help on this.

 BTW, is this the expected long-term solution? If so (and I don't think it
 should be <g>) the compiler really needs to toss an error, rather than
 emitting grossly unjust access-violators ...

It's the long term solution <g>. The reason for the null result is to mimic the behavior of dynamic_cast in C++.
Apr 05 2004
prev sibling next sibling parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
Whoops -- I meant to add why I feel this should not be the accepted
procedure:

The solution provided works fine to skirt the issue at hand, but the
methodology requires that the base-class (or base-interface) is
pre-configured to know about *all possible derivations* (subclasses). That,
is simply unrealistic.

- Kris

"Walter" <walter digitalmars.com> wrote in message
news:c4sk0f$2eon$1 digitaldaemon.com...
 The problem here is that all an interface is, when implemented, is a

 to a vtbl[]. There's no way the runtime can tell where it came from, so
 there is no way to upcast it.

 The way to deal with this is to add a member function to the interface

 is equivalent to the 'ClassFactory' function in COM programming. Then, in
 the implementation of that function, which does know the object type
 enclosing the interface, the cast can be done.


Apr 05 2004
parent reply "Kris" <someidiot earthlink.dot.dot.dot.net> writes:
Please forgive my gratuitous overuse of this NG ...

Thinking about this a little more Walter, if the base-interface were to
implement just the upcast to a concrete Object, then dynamic-casting should
handle it from there. For example:

interface IConduit
{
    Object  toObject();
}

class SocketConduit : IConduit
{
    Object  toObject() { return this; }
}

char[] getRemoteAddress(IConduit conduit)
{
    SocketConduit socket = cast(SocketConduit) conduit.toObject();
}

This, at least, is extensible. Though I'm torn as to whether it's the
right-thing-to-do <g>

Note:  one might be tempted to implement this instead as:

interface IConduit
{
    Conduit  toConduit();
}

...assuming there were a base-class implementation for IConduit.

However, that doesn't lend itself toward a sense of 'pattern' (let alone the
potential "forward reference" issues that an Interfaces is handy for
resolving).

If, in fact, you were to decide the suggested approach is the long-term
solution, then you might consider adding a hidden toObject() declaration to
each interface, and the corresponding "return this;" implementation to each
of the appropriate classes. If so, toObject() could thus become a property
of any Interface. Not too sure about that attitude though.





"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:c4skns$2fu9$1 digitaldaemon.com...
 The solution provided works fine to skirt the issue at hand, but the
 methodology requires that the base-class (or base-interface) is
 pre-configured to know about *all possible derivations* (subclasses).

 is simply unrealistic.

Apr 05 2004
parent "Walter" <walter digitalmars.com> writes:
I think this idea of yours will work fine. A hidden method cannot be added,
though, because then it would not work as a COM object.

"Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
news:c4smb9$2imr$1 digitaldaemon.com...
 Please forgive my gratuitous overuse of this NG ...

 Thinking about this a little more Walter, if the base-interface were to
 implement just the upcast to a concrete Object, then dynamic-casting

 handle it from there. For example:

 interface IConduit
 {
     Object  toObject();
 }

 class SocketConduit : IConduit
 {
     Object  toObject() { return this; }
 }

 char[] getRemoteAddress(IConduit conduit)
 {
     SocketConduit socket = cast(SocketConduit) conduit.toObject();
 }

 This, at least, is extensible. Though I'm torn as to whether it's the
 right-thing-to-do <g>

 Note:  one might be tempted to implement this instead as:

 interface IConduit
 {
     Conduit  toConduit();
 }

 ...assuming there were a base-class implementation for IConduit.

 However, that doesn't lend itself toward a sense of 'pattern' (let alone

 potential "forward reference" issues that an Interfaces is handy for
 resolving).

 If, in fact, you were to decide the suggested approach is the long-term
 solution, then you might consider adding a hidden toObject() declaration

 each interface, and the corresponding "return this;" implementation to

 of the appropriate classes. If so, toObject() could thus become a property
 of any Interface. Not too sure about that attitude though.





 "Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
 news:c4skns$2fu9$1 digitaldaemon.com...
 The solution provided works fine to skirt the issue at hand, but the
 methodology requires that the base-class (or base-interface) is
 pre-configured to know about *all possible derivations* (subclasses).

 is simply unrealistic.


Apr 05 2004
prev sibling next sibling parent reply Hauke Duden <H.NS.Duden gmx.net> writes:
Walter wrote:
 The problem here is that all an interface is, when implemented, is a pointer
 to a vtbl[]. There's no way the runtime can tell where it came from, so
 there is no way to upcast it.

"No way", as in "impossible", or in "no way" as in "not for 1.0"? ;) Couldn't there be a virtual function "Object __getObject()" that is automatically generated for all classes? Then the cast operator could simply call this function when casting an interface to an object. Seems like the only way to have the cast operator behave consistently. Hauke
Apr 05 2004
parent reply "Walter" <walter digitalmars.com> writes:
"Hauke Duden" <H.NS.Duden gmx.net> wrote in message
news:c4snu4$2kv8$1 digitaldaemon.com...
 Walter wrote:
 The problem here is that all an interface is, when implemented, is a


 to a vtbl[]. There's no way the runtime can tell where it came from, so
 there is no way to upcast it.

"No way", as in "impossible", or in "no way" as in "not for 1.0"? ;) Couldn't there be a virtual function "Object __getObject()" that is automatically generated for all classes? Then the cast operator could simply call this function when casting an interface to an object. Seems like the only way to have the cast operator behave consistently.

It could, but then it would be incompatible with COM programming.
Apr 05 2004
parent reply C <dont respond.com> writes:
 It could, but then it would be incompatible with COM programming.

Forgive my ignorance, but why would it be incompatible ? C On Mon, 5 Apr 2004 16:06:30 -0700, Walter <walter digitalmars.com> wrote= :
 "Hauke Duden" <H.NS.Duden gmx.net> wrote in message
 news:c4snu4$2kv8$1 digitaldaemon.com...
 Walter wrote:
 The problem here is that all an interface is, when implemented, is =



 pointer
 to a vtbl[]. There's no way the runtime can tell where it came from=



 so
 there is no way to upcast it.

"No way", as in "impossible", or in "no way" as in "not for 1.0"? ;) Couldn't there be a virtual function "Object __getObject()" that is automatically generated for all classes? Then the cast operator could=


 simply call this function when casting an interface to an object. See=


 like the only way to have the cast operator behave consistently.

It could, but then it would be incompatible with COM programming.

-- = D Newsgroup.
Apr 06 2004
parent Hauke Duden <H.NS.Duden gmx.net> writes:
C wrote:
 It could, but then it would be incompatible with COM programming.

Forgive my ignorance, but why would it be incompatible ?

I guess what Walter means is that then it would be very hard to cast a COM object into a D interface, since COM interfaces don't have a delete or toObject method by default. But the thing is, I believe this kind of problem already exists and cannot be circumvented. COM objects cannot be collected by the garbage collector, since the COM rules state that a Release call is the only way a COM object can be deleted. So you cannot explicitly delete COM objects and you cannot leave them to be collected. You HAVE to do your own memory management, i.e. you cannot treat them like other D interfaces. So, since COM and D interfaces are already incompatible I fail to see the reason why further incompatibilities should be a reason to restrict D. What this all comes down to is that you ALREADY have to be aware that you deal with a COM interface instead of a D interface and treat it differently. So you can just as well remember that toObject or delete will return null / have no effect, whatever. Hauke
Apr 06 2004
prev sibling next sibling parent "Ben Hinkle" <bhinkle4 juno.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:c4sk0f$2eon$1 digitaldaemon.com...
 The problem here is that all an interface is, when implemented, is a

 to a vtbl[]. There's no way the runtime can tell where it came from, so
 there is no way to upcast it.

I don't know the internals all that well, but at runtime the object implementing the interface knows its class, right? Isn't that how dynamic casting works? Is the problem that there exist "ClassInfo" but no "InterfaceInfo"? -Ben
Apr 06 2004
prev sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
As Kris notes in a reply elsewhere, this is problematic because then the 
interface (and all classes which implement the interface) must know all 
derived classes.  How about this mechanism instead, which (if it works) 
could easily be implemented automatically by the compiler:

interface IConduit
{
	Object getObject();
}
class SocketConduit : IConduit
{
	Object getObject() { return this; }
}

char[] getRemoteAddress(IConduit conduit)
{
	SocketConduit socket = cast(SocketConduit)conduit.getObject();
}

That is, every interface includes a function which will return an Object 
reference...then normal D casting mechanisms can upcast the Object to 
whatever class you think it is.

IMHO, this should be the automatic behavior when you try to cast an 
interface to a class...the compiler should do this for you.

Thoughts?

Walter wrote:
 The problem here is that all an interface is, when implemented, is a pointer
 to a vtbl[]. There's no way the runtime can tell where it came from, so
 there is no way to upcast it.
 
 The way to deal with this is to add a member function to the interface that
 is equivalent to the 'ClassFactory' function in COM programming. Then, in
 the implementation of that function, which does know the object type
 enclosing the interface, the cast can be done.
 
 interface IConduit
 {
     SocketConduit isSocketConduit();
 }
 
 class SocketConduit : IConduit
 {
     SocketConduit isSocketConduit() { return this; }
 }
 
 char[] getRemoteAddress(IConduit conduit)
 {
     SocketConduit socket = conduit.isSocketConduit();
 }
 
 
 "Kris" <someidiot earthlink.dot.dot.dot.net> wrote in message
 news:c4qi35$24f8$1 digitaldaemon.com...
 
I have a concrete-class that implements a generic interface. On occasion,
said interface needs to be upcast to the concrete-class to access
specialized functionality. Check out the cast(SocketConduit) code below

...
it explicitly sets the lValue to zero.  Good Grief ...

112:          char[] getRemoteAddress (IConduit conduit)
00403290   enter       0Ch,0
00403294   mov         dword ptr [ebp-0Ch],eax
113:          {
114:                  SocketConduit socket = cast(SocketConduit) conduit;
00403297   xor         eax,eax
00403299   mov         dword ptr [socket],eax


Apr 06 2004