Thread overview | |||||||||
---|---|---|---|---|---|---|---|---|---|
|
March 06, 2014 Forwarding or merging 'this' into a child class to aid chaining methods? | ||||
---|---|---|---|---|
| ||||
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 Re: Forwarding or merging 'this' into a child class to aid chaining methods? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Gary Willoughby | 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 Re: Forwarding or merging 'this' into a child class to aid chaining methods? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Gary Willoughby | 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 Re: Forwarding or merging 'this' into a child class to aid chaining methods? | ||||
---|---|---|---|---|
| ||||
Posted in reply to anonymous | 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 Re: Forwarding or merging 'this' into a child class to aid chaining methods? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Gary Willoughby | 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 Re: Forwarding or merging 'this' into a child class to aid chaining methods? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | 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 Re: Forwarding or merging 'this' into a child class to aid chaining methods? | ||||
---|---|---|---|---|
| ||||
Posted in reply to anonymous | 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.
|
Copyright © 1999-2021 by the D Language Foundation