www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - toString() through interface

reply David Held <dmd wyntrmute.com> writes:
interface Foo { }

class Bar : Foo
{
     override string toString() pure const { return "Bar"; }
}

void main()
{
     Foo foo = new Bar;
     foo.toString();
}

src\Bug.d(14): Error: no property 'toString' for type 'Bug.Foo'

Since all implementations of an interface must derive from Object, why 
can't we access Object's methods through the interface?  Even explicitly 
casting to Object doesn't help:

     cast(Object)(foo).toString();

Same error message.  Now here is where things get weird:

     Object baz = foo;

src\Bug.d(15): Error: cannot implicitly convert expression (foo) of type 
Bug.Foo to object.Object

Orly?!?  I'm pretty sure that almost every other language with 
interfaces and a singly-rooted object hierarchy has implicit conversions 
from an interface to the base object type.  Oddly enough, an explicit 
cast "fixes" things:

     Object baz = cast(Object) foo;
     baz.toString(); // OK

What doesn't make sense is why the inline cast of foo is not allowed, 
even though it seems to me that it should have the exact same effect. 
Am I missing something here?

Dave
Apr 19 2014
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sunday, 20 April 2014 at 00:35:30 UTC, David Held wrote:
 Since all implementations of an interface must derive from 
 Object
That's not true. They can also come from IUnknown or a C++ interface.
     cast(Object)(foo).toString();
(cast(Object)foo).toString() might work This might return null tho if it is a non-D object through the interface!
Apr 19 2014
next sibling parent David Held <dmd wyntrmute.com> writes:
On 4/19/2014 5:45 PM, Adam D. Ruppe wrote:
 On Sunday, 20 April 2014 at 00:35:30 UTC, David Held wrote:
 Since all implementations of an interface must derive from Object
That's not true. They can also come from IUnknown or a C++ interface.
Ok, that's a good reason!
     cast(Object)(foo).toString();
(cast(Object)foo).toString() might work This might return null tho if it is a non-D object through the interface!
Yes, I later discovered that I didn't understand the cast syntax properly. This does indeed work, but leads to the discovery that Object.toString() is not pure. :( But that's tolerable. Also, declaring toString() in the interfaces avoids the need for a cast entirely, although I feel this is a bit of a dirty hack. Dave
Apr 19 2014
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sat, 19 Apr 2014 20:45:50 -0400, Adam D. Ruppe  
<destructionator gmail.com> wrote:

 On Sunday, 20 April 2014 at 00:35:30 UTC, David Held wrote:
 Since all implementations of an interface must derive from Object
That's not true. They can also come from IUnknown or a C++ interface.
This is also not true. Only interfaces derived from IUnknown point to a COM object. Only interfaces marked as extern(C++) can point to a C++ object. Any interface that is purely D MUST point at a D object that derives from Object, EVEN D-defined COM or C++ objects (the latter doesn't really exist). His code should compile IMO.
     cast(Object)(foo).toString();

 (cast(Object)foo).toString() might work


 This might return null tho if it is a non-D object through the interface!
As stated above, this is not possible. -Steve
Apr 21 2014
prev sibling parent reply David Held <dmd wyntrmute.com> writes:
On 4/19/2014 5:35 PM, David Held wrote:
 interface Foo { }

 class Bar : Foo
 {
      override string toString() pure const { return "Bar"; }
 }

 void main()
 {
      Foo foo = new Bar;
      foo.toString();
 }
To make things more interesting, consider the call to toString() from inside a class (which is closer to my actual use case): class Baz { override string toString() pure const { return cast(Object)(foo).toString(); } Foo foo; } This really makes the compiler go bonkers: src\Bug.d(11): Error: pure nested function 'toString' cannot access mutable data 'foo' src\Bug.d(11): Error: pure nested function 'toString' cannot access mutable data 'foo' src\Bug.d(11): Error: no property 'toString' for type 'Bug.Foo' src\Bug.d(11): Error: pure nested function 'toString' cannot access mutable data 'foo' src\Bug.d(11): Error: need 'this' for 'foo' of type 'Bug.Foo' Apparently, DMD features "high-availability error reporting", because the first message might not be received by the programmer, so it writes it again, then gives you a different message, and writes it one more time, just in case there was a communication error or some other fault preventing you from receiving this important message about access to 'foo'. Again, the cast appears to do absolutely nothing, as the compiler insists on looking up toString() in Foo instead of Object. What is really peculiar is that message 1, 2, and 4 are complaining that Baz.toString() is not allowed to access foo because it is mutable. And yet, the 5th error message tells us how to fix it: just add 'this.': override string toString() pure const { return cast(Object)(this.foo).toString(); } Now we just get this: src\Bug.d(11): Error: no property 'toString' for type 'const(Foo)' Hmm...so adding 'this.' changes a field from "unacceptably mutable" to "just fine for this pure const method". Isn't that weird?!? Note also how the error message has subtly changed from "Bug.Foo" to "const(Foo)", because the compiler is still stubbornly insisting on ignoring the cast to 'Object'. Is there a rational explanation for all this behavior?? Dave
Apr 19 2014
next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
David Held:

 class Baz
 {
     override string toString() pure const
     { return cast(Object)(foo).toString(); }

     Foo foo;
 }

 This really makes the compiler go bonkers:

 src\Bug.d(11): Error: pure nested function 'toString' cannot 
 access mutable data 'foo'
 src\Bug.d(11): Error: pure nested function 'toString' cannot 
 access mutable data 'foo'
 src\Bug.d(11): Error: no property 'toString' for type 'Bug.Foo'
 src\Bug.d(11): Error: pure nested function 'toString' cannot 
 access mutable data 'foo'
 src\Bug.d(11): Error: need 'this' for 'foo' of type 'Bug.Foo'

 Apparently, DMD features "high-availability error reporting", 
 because the first message might not be received by the 
 programmer, so it writes it again, then gives you a different 
 message, and writes it one more time, just in case there was a 
 communication error or some other fault preventing you from 
 receiving this important message about access to 'foo'.
Your code is probably wrong, but those error messages seem too many. So I suggest you to open a little bug report, asking for less error messages :-)
 Is there a rational explanation for all this behavior??
Try also: override string toString() const { return (cast(Object)foo).toString(); } You still have to understand some things about the D objects and interfaces, their standard methods, and what const and purity do in D. Bye, bearophile
Apr 19 2014
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sat, 19 Apr 2014 20:51:49 -0400, David Held <dmd wyntrmute.com> wrote:

 On 4/19/2014 5:35 PM, David Held wrote:
 interface Foo { }

 class Bar : Foo
 {
      override string toString() pure const { return "Bar"; }
 }

 void main()
 {
      Foo foo = new Bar;
      foo.toString();
 }
To make things more interesting, consider the call to toString() from inside a class (which is closer to my actual use case): class Baz { override string toString() pure const { return cast(Object)(foo).toString(); } Foo foo; } This really makes the compiler go bonkers: src\Bug.d(11): Error: pure nested function 'toString' cannot access mutable data 'foo' src\Bug.d(11): Error: pure nested function 'toString' cannot access mutable data 'foo' src\Bug.d(11): Error: no property 'toString' for type 'Bug.Foo' src\Bug.d(11): Error: pure nested function 'toString' cannot access mutable data 'foo' src\Bug.d(11): Error: need 'this' for 'foo' of type 'Bug.Foo' Apparently, DMD features "high-availability error reporting", because the first message might not be received by the programmer, so it writes it again, then gives you a different message, and writes it one more time, just in case there was a communication error or some other fault preventing you from receiving this important message about access to 'foo'. Again, the cast appears to do absolutely nothing, as the compiler insists on looking up toString() in Foo instead of Object. What is really peculiar is that message 1, 2, and 4 are complaining that Baz.toString() is not allowed to access foo because it is mutable. And yet, the 5th error message tells us how to fix it: just add 'this.': override string toString() pure const { return cast(Object)(this.foo).toString(); } Now we just get this: src\Bug.d(11): Error: no property 'toString' for type 'const(Foo)'
To explain what the compiler is having trouble with, you have to understand precedence. cast(Object) does not come before '.' So what the compiler thinks you said is: return cast(Object)(this.foo.toString()); Others have said how to fix it. I just wanted to point out why you need to fix it that way. -Steve
Apr 21 2014