Thread overview
Forwarding or merging 'this' into a child class to aid chaining methods?
Mar 06, 2014
Gary Willoughby
Mar 06, 2014
Ali Çehreli
Mar 06, 2014
anonymous
Mar 06, 2014
Ali Çehreli
Mar 06, 2014
anonymous
Mar 06, 2014
Gary Willoughby
March 06, 2014
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.

March 06, 2014
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

March 06, 2014
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
March 06, 2014
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

March 06, 2014
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
March 06, 2014
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();
March 06, 2014
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.