www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Forwarding or merging 'this' into a child class to aid chaining

reply "Gary Willoughby" <dev nomad.so> writes:
I'm trying to create methods across class hierarchies that can be 
chained nicely but i'm running into the problem that 'this' 
declared in a parent class only refers to that type. Is there a 
way i can get the following code to perform in the way i expect?

import std.stdio;

class Foo
{
	public auto foo()
	{
		return this;
	}
}

class Bar : Foo
{
	public auto bar()
	{
		return this;
	}
}

void main(string[] args)
{
	Bar bar = new Bar().bar().foo();
}

test2.d(21): Error: cannot implicitly convert expression ((new 
Bar).bar().foo()) of type test2.Foo to test2.Bar
Failed: 'dmd' '-v' '-o-' 'test2.d' '-I.'

How can i make the above marked 'this' refer to Bar when being 
called in a chain? When i call the methods like this each method 
call seems to implicitly convert 'this' into that method's 
containing class' instance, breaking the code and sometimes 
hiding child methods.
Mar 06 2014
next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/06/2014 11:04 AM, Gary Willoughby wrote:
 I'm trying to create methods across class hierarchies that can be
 chained nicely but i'm running into the problem that 'this' declared in
 a parent class only refers to that type. Is there a way i can get the
 following code to perform in the way i expect?

 import std.stdio;

 class Foo
 {
      public auto foo()
      {
          return this;
      }
 }

 class Bar : Foo
 {
      public auto bar()
      {
          return this;
      }
 }

 void main(string[] args)
 {
      Bar bar = new Bar().bar().foo();
 }

 test2.d(21): Error: cannot implicitly convert expression ((new
 Bar).bar().foo()) of type test2.Foo to test2.Bar
 Failed: 'dmd' '-v' '-o-' 'test2.d' '-I.'

 How can i make the above marked 'this' refer to Bar when being called in
 a chain? When i call the methods like this each method call seems to
 implicitly convert 'this' into that method's containing class' instance,
 breaking the code and sometimes hiding child methods.
I had the exact problem in C++ which I have solved with the help of boost::shared_ptr, boost::enable_shared_from_this, and by passing down the most derived pointer type as a template parameter: template <class MostDerived> class FooImpl : public FooInterface, public boost::enable_shared_from_this<MostDerived> { // ... protected: typedef boost::shared_ptr<MostDerived> MostDerivedPtr; MostDerivedPtr foo()(); // <-- HERE, the return type is // the most derived type }; Let's try something similar for D: import std.stdio; interface Foo { // ... needed if there is non-specific interface ... } class FooImpl(MostDerived) : Foo { public MostDerived foo() { return cast(MostDerived)this; } } class Bar : FooImpl!Bar { public Bar bar() { return this; } } void main(string[] args) { Bar bar = new Bar().bar().foo(); } Ali
Mar 06 2014
prev sibling next sibling parent reply "anonymous" <anonymous example.com> writes:
On Thursday, 6 March 2014 at 19:04:50 UTC, Gary Willoughby wrote:
 I'm trying to create methods across class hierarchies that can 
 be chained nicely but i'm running into the problem that 'this' 
 declared in a parent class only refers to that type. Is there a 
 way i can get the following code to perform in the way i expect?

 import std.stdio;

 class Foo
 {
 	public auto foo()
 	{
 		return this;
 	}
 }

 class Bar : Foo
 {
 	public auto bar()
 	{
 		return this;
 	}
 }

 void main(string[] args)
 {
 	Bar bar = new Bar().bar().foo();
 }

 test2.d(21): Error: cannot implicitly convert expression ((new 
 Bar).bar().foo()) of type test2.Foo to test2.Bar
 Failed: 'dmd' '-v' '-o-' 'test2.d' '-I.'

 How can i make the above marked 'this' refer to Bar when being 
 called in a chain? When i call the methods like this each 
 method call seems to implicitly convert 'this' into that 
 method's containing class' instance, breaking the code and 
 sometimes hiding child methods.
public auto foo(this T)() { return cast(T) this; } http://dlang.org/template.html#TemplateThisParameter
Mar 06 2014
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/06/2014 11:16 AM, anonymous wrote:

      public auto foo(this T)()
      {
          return cast(T) this;
      }

 http://dlang.org/template.html#TemplateThisParameter
Sweet! :) Unfortunately, it has a somewhat obfuscated definition: "TemplateThisParameters are used in member function templates to pick up the type of the this reference." "type of the this reference" does not explain that it is about the type of the most derived object. Ali
Mar 06 2014
parent "anonymous" <anonymous example.com> writes:
On Thursday, 6 March 2014 at 19:30:25 UTC, Ali Çehreli wrote:
 On 03/06/2014 11:16 AM, anonymous wrote:
[...]
 http://dlang.org/template.html#TemplateThisParameter
[...]
 "type of the this reference" does not explain that it is about 
 the type of the most derived object.
Well, it isn't. It works with new Bar().bar().foo(), because new Bar().bar() is a Bar. This wouldn't work: Foo foo = new Bar().bar(); Bar bar = foo.foo();
Mar 06 2014
prev sibling parent "Gary Willoughby" <dev nomad.so> writes:
On Thursday, 6 March 2014 at 19:16:07 UTC, anonymous wrote:
 	public auto foo(this T)()
 	{
 		return cast(T) this;
 	}

 http://dlang.org/template.html#TemplateThisParameter
Nice! This seems to be what i was after and it works well.
Mar 06 2014
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 06 Mar 2014 14:04:48 -0500, Gary Willoughby <dev nomad.so> wrote:

 I'm trying to create methods across class hierarchies that can be  
 chained nicely but i'm running into the problem that 'this' declared in  
 a parent class only refers to that type. Is there a way i can get the  
 following code to perform in the way i expect?

 import std.stdio;

 class Foo
 {
 	public auto foo()
 	{
 		return this;
 	}
 }

 class Bar : Foo
 {
 	public auto bar()
 	{
 		return this;
 	}
 }

 void main(string[] args)
 {
 	Bar bar = new Bar().bar().foo();
 }

 test2.d(21): Error: cannot implicitly convert expression ((new  
 Bar).bar().foo()) of type test2.Foo to test2.Bar
 Failed: 'dmd' '-v' '-o-' 'test2.d' '-I.'

 How can i make the above marked 'this' refer to Bar when being called in  
 a chain? When i call the methods like this each method call seems to  
 implicitly convert 'this' into that method's containing class' instance,  
 breaking the code and sometimes hiding child methods.
There are two possibilities. First, you can create a non-virtual function, which can be templated on the type called with: T foo(this T)() // T == whatever type you called the method as. { return cast(T)this; // the cast is necessary because inside foo, 'this' is base class type } Second, you can override the function in subsequent classes: class Bar : Foo { public auto foo() { return this;} } And of course, if you just want to inherit the implementation: public auto foo() { return cast(Foo)super.foo();} This gets tricky if you are not certain of the base implementation. If it could ever return a "Foo" that is not actually 'this', then the function will likely not work correctly. I had proposed, a long time ago, a mechanism to note that a function should always return 'this', and the compiler could take that into account (enforce it, and make assumptions based on the type called with). The response I got was to use the template form. -Steve
Mar 06 2014