www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Q: How to return sub class from base class method

reply Myron Alexander <someone somewhere.com> writes:
Hello.

I have a class structure as such:

 class A {
    typeof(this) doSomething (????) {
       ...
       return this;
    }
 }
 
 class B : A {
    typeof(this) doSomethingElse (????) {
       ...
       return this;
    }
 }
 
 void main () {
    // Fails
    B b = (new B()).doSomething (???).doSomethingElse (???);
 }

The method chain fails as doSomething returns type A. I want to define method doSomething in such a way that it will return the specialized type (B) rather than the base type. I'm currently using a mixin to mixin overriding code:
 template doSomethingOverride () {
    override typeof(this) doSomething (????) {
       super.doSomething (????);
       return this;
    }
 }

Is there a way to set the return type as the type instantiated? Thanks, Myron.
Jun 17 2007
parent reply Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
Myron Alexander wrote:

 I have a class structure as such:
 
 class A {
    typeof(this) doSomething (????) {
       ...
       return this;
    }
 }
 
 class B : A {
    typeof(this) doSomethingElse (????) {
       ...
       return this;
    }
 }
 
 void main () {
    // Fails
    B b = (new B()).doSomething (???).doSomethingElse (???);
 }

The method chain fails as doSomething returns type A. I want to define method doSomething in such a way that it will return the specialized type (B) rather than the base type. Is there a way to set the return type as the type instantiated?

Yes, you can use CRTP (http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern). class A(T) { T doSomething() { ... return cast(T)this; } } class B(T) : A!(T) { T doSomethingElse() { ... return cast(T)this; } } There might be other (more clever) ways to do this too. Like a mixin for the "selftype" or something.
Jun 17 2007
next sibling parent Myron Alexander <someone somewhere.com> writes:
Jari-Matti Mäkelä wrote:
 
 Yes, you can use CRTP
 (http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern).
 
 class A(T) {
   T doSomething() {
     ...
     return cast(T)this;
   }
 }
 
 class B(T) : A!(T) {
   T doSomethingElse() {
     ...
     return cast(T)this;
   }
 }
 
 There might be other (more clever) ways to do this too. Like a mixin for
 the "selftype" or something.

Thanks for the suggestion. I am hoping for a solution that does not require me to set a type on instantiation. I just want to write: B b = (new B()).doSomething (???).doSomethingElse (???); If the language does not have a way to do it, that's ok, the mixin solution works. Regards, Myron.
Jun 17 2007
prev sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Jari-Matti Mäkelä wrote:
 Myron Alexander wrote:
 
 
I have a class structure as such:


class A {
   typeof(this) doSomething (????) {
      ...
      return this;
   }
}

class B : A {
   typeof(this) doSomethingElse (????) {
      ...
      return this;
   }
}

void main () {
   // Fails
   B b = (new B()).doSomething (???).doSomethingElse (???);
}

The method chain fails as doSomething returns type A. I want to define method doSomething in such a way that it will return the specialized type (B) rather than the base type. Is there a way to set the return type as the type instantiated?

Yes, you can use CRTP (http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern). class A(T) { T doSomething() { ... return cast(T)this; } } class B(T) : A!(T) { T doSomethingElse() { ... return cast(T)this; } } There might be other (more clever) ways to do this too. Like a mixin for the "selftype" or something.

I believe B should look like this, if you're using that pattern: class B : A!(B) { B doSomethingElse() { // ... return this; } } Thus, B is derived from a class template to which you pass B as a parameter. This is often used in C++ as a way of emulating mixin-like behavior. Thus, D's template mixins could very well be a better solution. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Jun 17 2007
parent reply Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
Kirk McDonald wrote:

 Jari-Matti Mäkelä wrote:
 class A(T) {
   T doSomething() {
     ...
     return cast(T)this;
   }
 }
 
 class B(T) : A!(T) {
   T doSomethingElse() {
     ...
     return cast(T)this;
   }
 }
 
 There might be other (more clever) ways to do this too. Like a mixin for
 the "selftype" or something.

I believe B should look like this, if you're using that pattern: class B : A!(B) { B doSomethingElse() { // ... return this; } }

Actually no - class B was supposed to be in the middle of the hierarchy so class C would have looked like that :) Something like class A(T = A) { ... } class B(T = B) : A!(T) { ... } would have been nice for those non-abstract base classes so they could have been instantiated too, but apparently the compiler didn't want to co-operate this time.
 
 Thus, B is derived from a class template to which you pass B as a
 parameter. This is often used in C++ as a way of emulating mixin-like
 behavior. Thus, D's template mixins could very well be a better solution.
 

Feel free to create a better solution :)
Jun 17 2007
parent Myron Alexander <someone somewhere.com> writes:
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 8bit

Jari-Matti Mäkelä wrote:
 Kirk McDonald wrote:
 
 Jari-Matti Mäkelä wrote:
 class A(T) {
   T doSomething() {
     ...
     return cast(T)this;
   }
 }

 class B(T) : A!(T) {
   T doSomethingElse() {
     ...
     return cast(T)this;
   }
 }

 There might be other (more clever) ways to do this too. Like a mixin for
 the "selftype" or something.

class B : A!(B) { B doSomethingElse() { // ... return this; } }

Actually no - class B was supposed to be in the middle of the hierarchy so class C would have looked like that :) Something like class A(T = A) { ... } class B(T = B) : A!(T) { ... } would have been nice for those non-abstract base classes so they could have been instantiated too, but apparently the compiler didn't want to co-operate this time.
 Thus, B is derived from a class template to which you pass B as a
 parameter. This is often used in C++ as a way of emulating mixin-like
 behavior. Thus, D's template mixins could very well be a better solution.

Feel free to create a better solution :)

Thanks all. I am happy with the mixin solution, even if I forget to add the mixin, the compiler will generate an error when I try to chain the class methods after the doSomething so it's not a dangerous one at that. Just for your information, I attached the test source where I am using it. The doSomething method here is called 'initCause'. Regards, Myron.
Jun 18 2007