www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Signals far from Slots

reply Jim Hewes <jimhewes gmail.com> writes:
Is there a good way to connect signals and slots when the objects are 
far apart? All tutorials for signals and slots show the objects being 
readily accessible by the main() function. But what if they're not? Is 
there an elegant design?

For example, here's a typical minimal demo:

---------------------------------------------------------
class Foo
{
     void Listen()
     {
         writeln("Foo: I hear you!");
     }
}

class Bar
{
     mixin Signal!() barSignal;
     void SaySomething()
     {    emit();   }
}

int main(string[] argv)
{
     Foo f = new Foo();
     Bar b = new Bar();

     b.barSignal.connect(&f.Listen);
     b.SaySomething();
     return 0;
}
---------------------------------------------------------------

The main() function is a kind of controller that connects up the 
components. But let's say main() doesn't have access to Bar because it's 
behind a layer:

------------------------------------------------------------------

class BarContainer
{
     this() { b = new Bar(); }

     void SetBarSignalHandler(b.barSignal.slot_t dg)
     {    b.barSignal.connect(dg);  }

     private Bar b;
};

int main(string[] argv)
{
     Foo f = new Foo();
     BarContainer bc = new BarContainer();

     bc.SetBarSignalHandler(&f.Listen);
     return 0;
}
------------------------------------------------------------------
This can get worse if there is also a FooContainer and objects get 
further away.

My first thought is to pass the delegates through the layers, as in the 
SetBarSignalHandler() function in BarContainer. But this seems kind of 
ugly as all the layers need to know about the connection between the 
signaling object and the observer object. It would be nice if they 
didn't have to.

Do you know any any cleaner way?

Jim
May 04 2014
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 05/04/2014 10:57 AM, Jim Hewes wrote:
 Is there a good way to connect signals and slots when the objects are
 far apart? All tutorials for signals and slots show the objects being
 readily accessible by the main() function. But what if they're not? Is
 there an elegant design?

 For example, here's a typical minimal demo:

 ---------------------------------------------------------
 class Foo
 {
      void Listen()
      {
          writeln("Foo: I hear you!");
      }
 }

 class Bar
 {
      mixin Signal!() barSignal;
      void SaySomething()
      {    emit();   }
 }

 int main(string[] argv)
 {
      Foo f = new Foo();
      Bar b = new Bar();

      b.barSignal.connect(&f.Listen);
      b.SaySomething();
      return 0;
 }
 ---------------------------------------------------------------

 The main() function is a kind of controller that connects up the
 components. But let's say main() doesn't have access to Bar because it's
 behind a layer:

 ------------------------------------------------------------------

 class BarContainer
 {
      this() { b = new Bar(); }

      void SetBarSignalHandler(b.barSignal.slot_t dg)
      {    b.barSignal.connect(dg);  }

      private Bar b;
 };

 int main(string[] argv)
 {
      Foo f = new Foo();
      BarContainer bc = new BarContainer();

      bc.SetBarSignalHandler(&f.Listen);
      return 0;
 }
 ------------------------------------------------------------------
 This can get worse if there is also a FooContainer and objects get
 further away.

 My first thought is to pass the delegates through the layers, as in the
 SetBarSignalHandler() function in BarContainer. But this seems kind of
 ugly as all the layers need to know about the connection between the
 signaling object and the observer object. It would be nice if they
 didn't have to.

 Do you know any any cleaner way?

 Jim
As far as I understand, you have to expose a function on BarContainer similar to SaySomething() (I named it SaySomething() as well :) ): import std.stdio; import std.signals; class Foo { void Listen() { writeln("Foo: I hear you!"); } } class Bar { mixin Signal!() barSignal; void SaySomething() { emit(); } } class BarContainer { this() { b = new Bar(); } void SetBarSignalHandler(b.barSignal.slot_t dg) { b.barSignal.connect(dg); } void SaySomething() // <== ADDED { b.emit(); } private Bar b; } int main(string[] argv) { Foo f = new Foo(); BarContainer bc = new BarContainer(); bc.SetBarSignalHandler(&f.Listen); bc.SaySomething(); // <== ADDED return 0; } Ali
May 05 2014
parent Jim Hewes <jimhewes gmail.com> writes:
Thanks Ali,

In the second part of that example I was hoping it was understood that 
Bar generates it's own signal. Sorry, I guess I wasn't clear; I was just 
trying to reduce code.

I think maybe I'm really looking for a way that BarContainer doesn't 
have to know that Bar and Foo are connected. One way I used to do this 
was to have a central EventManager object that all other objects knew 
about and it would be the traffic cop for events. So you have the 
traditional Java type of Observer implementation and then listeners 
register with the EventManager saying that they are interested in a type 
of event while signalers send all their events to the EventManager. 
Since signals and slots are intended to have some advantage over the 
Java way, I wondered if there was a better way to handle the setting up 
of the connection. (Signals and slots decreases coupling of objects, but 
then some controller object needs to know about them enough to connect 
them.)

Sorry, maybe this is the wrong place to ask such questions. I know there 
are plenty of forums for specific languages and APIs. But I don't know 
of any that just deal with general design, which is what I'd like to get 
better at.

BTW, thanks for your D tutorial! It's a big help to me.

Jim
May 08 2014