www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Covariant callback functions, or assigning base class members through

reply "Rene Zwanenburg" <renezwanenburg gmail.com> writes:
Given the following code:

class Base
{
	alias CallbackType = void delegate(Base);
	
	CallbackType callback;
	
	void foo()
	{
		callback(this);
	}
}

class Derived : Base
{

}

void main()
{
	auto d = new Derived();
	d.callback = (Derived d) { /* Do something */ }
}

Obviously this won't compile, since the callback function needs 
to have Base as parameter, not Derived. But the given code is 
perfectly safe because in main d typed as Derived, not Base. Does 
anyone know a clean way to support the code given in main(), 
preferably by defining some smart CallbackType so Derived doesn't 
need to be modified?

I understand this is unsafe in other scenarios, for example:

class AnotherSubClass : Base
{

}

void bar(Base b1, Base b2)
{
	b1.callback = b2.callback;
}

void doom()
{
	auto b1 = new Derived();
	auto b2 = new AnotherSubClass();
	b2.callback = (AnotherSubClass a) { };
	bar(b1, b2);
}

But the CallbackType should be able to prevent such unsafe 
assignments.
Jul 14 2015
next sibling parent reply "Kagamin" <spam here.lot> writes:
Template Base on Derived:

class Derived : Base!Derived
{

}
Jul 14 2015
parent reply "Rene Zwanenburg" <renezwanenburg gmail.com> writes:
On Tuesday, 14 July 2015 at 15:35:04 UTC, Kagamin wrote:
 Template Base on Derived:

 class Derived : Base!Derived
 {

 }
Sure, but that would make Base!Derived and Base!AnotherSubClass different types. What I'd like to end up with is a Base[], being able to call foo() on the array members. Other parts of the code will add instances of derived types to this array, and have set the callback function to a delegate accepting said derived type. This is part of a library I'm working on. I don't mind if the library itself contains some nastiness to get this to work, as long as user code, which defines the derived types, is type safe and clean.
Jul 14 2015
parent Jacob Carlborg <doob me.com> writes:
On 2015-07-14 17:53, Rene Zwanenburg wrote:

 Sure, but that would make Base!Derived and Base!AnotherSubClass
 different types.

 What I'd like to end up with is a Base[], being able to call foo() on
 the array members. Other parts of the code will add instances of derived
 types to this array, and have set the callback function to a delegate
 accepting said derived type.
You can add another base type that is not templated: abstract class Base { abstract void foo (); } abstract class TemplateBase(T) : Base { alias CallbackType = void delegate(T); CallbackType callback; override void foo () { callback(cast(T) this); } } class Derived : TemplateBase!(Derived) { void derivedFunc() { import std.stdio; writeln("Derived object in action..."); } } void main() { auto d = new Derived(); d.callback = (Derived d) { d.derivedFunc(); }; Base[] bases = [d]; foreach (b ; bases) b.foo(); } -- /Jacob Carlborg
Jul 15 2015
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/14/15 11:28 AM, Rene Zwanenburg wrote:
 Given the following code:

 class Base
 {
      alias CallbackType = void delegate(Base);

      CallbackType callback;

      void foo()
      {
          callback(this);
      }
 }

 class Derived : Base
 {

 }

 void main()
 {
      auto d = new Derived();
      d.callback = (Derived d) { /* Do something */ }
 }

 Obviously this won't compile, since the callback function needs to have
 Base as parameter, not Derived. But the given code is perfectly safe
 because in main d typed as Derived, not Base. Does anyone know a clean
 way to support the code given in main(), preferably by defining some
 smart CallbackType so Derived doesn't need to be modified?
No, this isn't possible. Only thing you can do is: d.callback = (Base b) { if(auto d = cast(Derived)b) { /* Do something */ } else assert(0); }; -Steve
Jul 14 2015
parent "Rene Zwanenburg" <renezwanenburg gmail.com> writes:
On Tuesday, 14 July 2015 at 17:26:32 UTC, Steven Schveighoffer 
wrote:
 On 7/14/15 11:28 AM, Rene Zwanenburg wrote:
 Given the following code:

 class Base
 {
      alias CallbackType = void delegate(Base);

      CallbackType callback;

      void foo()
      {
          callback(this);
      }
 }

 class Derived : Base
 {

 }

 void main()
 {
      auto d = new Derived();
      d.callback = (Derived d) { /* Do something */ }
 }

 Obviously this won't compile, since the callback function 
 needs to have
 Base as parameter, not Derived. But the given code is 
 perfectly safe
 because in main d typed as Derived, not Base. Does anyone know 
 a clean
 way to support the code given in main(), preferably by 
 defining some
 smart CallbackType so Derived doesn't need to be modified?
No, this isn't possible. Only thing you can do is: d.callback = (Base b) { if(auto d = cast(Derived)b) { /* Do something */ } else assert(0); }; -Steve
Thanks, I'll have to think of something else then. At my day job I mostly work with C#, and I strongly dislike how event handler parameters there have to be broader-typed (is that even a valid term?) than they logically need to be, which is why I'm trying to avoid doing something similar here.
Jul 14 2015
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2015-07-14 17:28, Rene Zwanenburg wrote:
 Given the following code:

 class Base
 {
      alias CallbackType = void delegate(Base);

      CallbackType callback;

      void foo()
      {
          callback(this);
      }
 }

 class Derived : Base
 {

 }

 void main()
 {
      auto d = new Derived();
      d.callback = (Derived d) { /* Do something */ }
 }

 Obviously this won't compile, since the callback function needs to have
 Base as parameter, not Derived.
You can cast the delegate. It's probably unsafe but a simple example works. -- /Jacob Carlborg
Jul 14 2015
prev sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/14/2015 08:28 AM, Rene Zwanenburg wrote:

 But the CallbackType should be able to prevent such unsafe assignments.
The following struct applies what others have recommended only if an actual derived type is provided. However, it is still unsafe as the direct assignment to 'callback' cannot know that the object is the same as template parameter D. struct CallbackBased(B) { alias Func = void delegate(B); Func func; void opAssign(D)(void delegate(D) arg) if (is (D : B)) { func = cast(Func)(arg); } void opCall(B obj) { func(obj); } } class Base { alias CallbackType = CallbackBased!Base; CallbackType callback; void foo() { callback(this); } } class Derived : Base { void derivedFunc() { import std.stdio; writeln("Derived object in action..."); } } void main() { auto d = new Derived(); d.callback = (Derived d) { d.derivedFunc(); }; d.foo(); } Ali
Jul 15 2015