www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - std.pattern.. templated publisher subscriber pattern, adding events

reply BLS <windevguy hotmail.de> writes:
Hi,
Andrei brings in the idea of std.pattern. Seems that this module is 
"stalled";  Unfortunately !
However I would like to enhance collection classes (likewise 
dcollections)  with a Publisher - Subscriber pattern (signal - slot, or 
observer pattern) , if you prefer)
Hope the idea of enhancing collections with events become clear with the 
following snippet.
---I am able to spend just a few hours a month with D programming.. in 
other words, please don't kill me ;)

Untested Draft code which requires a lot of help and ,more important, 
feedback from you.

struct publisherMsg(T) {
	T data;
	Action a;
}
mixin template Publisher(T) {
private :

	enum Action = {INSERT, UPDATE, DELETE, READONLY};	
	//alias typeof(this) PT;

	struct receiver {
		Object o;
		callback cb;	
		// opCall() or this() ?
	}

	receiver[] subscriber;
		
	publisherMsg!(T) msg;
	
	alias void delegate(ref msg) callback;		
	
	void addSubscriber(object o, callback cb) {
		//subscriber ~= o;
	}	
	
	void publish(T t, Action act) {
		foreach  (object o ; subscriber)
		{
		// create and send message

		}
	}	
}

mixin template Subscriber() {
	// see UndoList
}

final class Stack(T, bool observable = false ) /+ should use alias +/ {
	T[] data;	
	static if (observable)	mixin Observable!T;

	void push(T t) {
		publish(t, Action.INSERT)
	}
	T pop() {

	}	
	bool empty() {
		
	}
	T top() {
		
	}
	size_t size() {

	}
}


/// Undo list will receive every pushed or popped item (T and Action)
class UndoList(T) {
	private:
	T[] data;
	
	// should be part of the subscriber mixin template.
	publisherMsg!T stackMessage;

	void delegate(ref stackMessage) dg;
	
	alias Stack!(int) IntegerStack;
	IntegerStack intstack = new IntegerStack;
	
	private this() {
		dg = &this.feedback;
		
	}
	public void feedback(ref stackMessage msg ) {
		writefln("push or pop action");
	}
		
}
-Bjoern
Jul 06 2010
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 07.07.2010 2:01, BLS wrote:
 Hi,
 Andrei brings in the idea of std.pattern. Seems that this module is
 "stalled"; Unfortunately !
 However I would like to enhance collection classes (likewise
 dcollections) with a Publisher - Subscriber pattern (signal - slot, or
 observer pattern) , if you prefer)
 Hope the idea of enhancing collections with events become clear with the
 following snippet.
 ---I am able to spend just a few hours a month with D programming.. in
 other words, please don't kill me ;)

 Untested Draft code which requires a lot of help and ,more important,
 feedback from you.

 struct publisherMsg(T) {
 T data;
 Action a;
 }
 mixin template Publisher(T) {
 private :

 enum Action = {INSERT, UPDATE, DELETE, READONLY};
 //alias typeof(this) PT;

 struct receiver {
 Object o;
 callback cb;
 // opCall() or this() ?
 }

The object itself could be bound like this: struct MyObject{ string name; } MyObject mb = MyObject("test"); callback c = (ref msg m){ if(msg.action == INSERT) writeln("inserted " ~ mb.name); //.... }; And better yet it's real type is known inside delegate at binding point. Not sure why msg is ref parameter? So that one subscriber can affect msg that will in turn see another one - seems weird.
 receiver[] subscriber;

 publisherMsg!(T) msg;

 alias void delegate(ref msg) callback;

 void addSubscriber(object o, callback cb) {
 //subscriber ~= o;
 }

 void publish(T t, Action act) {
 foreach (object o ; subscriber)
 {
 // create and send message

 }
 }
 }

 mixin template Subscriber() {
 // see UndoList
 }

 final class Stack(T, bool observable = false ) /+ should use alias +/ {
 T[] data;
 static if (observable) mixin Observable!T;

 void push(T t) {
 publish(t, Action.INSERT)
 }
 T pop() {

 }
 bool empty() {

 }
 T top() {

 }
 size_t size() {

 }
 }


 /// Undo list will receive every pushed or popped item (T and Action)
 class UndoList(T) {
 private:
 T[] data;

 // should be part of the subscriber mixin template.
 publisherMsg!T stackMessage;

 void delegate(ref stackMessage) dg;

 alias Stack!(int) IntegerStack;
 IntegerStack intstack = new IntegerStack;

 private this() {
 dg = &this.feedback;

 }
 public void feedback(ref stackMessage msg ) {
 writefln("push or pop action");
 }

 }
 -Bjoern

-- Dmitry Olshansky
Jul 06 2010
parent BLS <windevguy hotmail.de> writes:
Hi Dimitry, thanks for the feedback!
Please have a look at the new snippet (just posted)

msg is a ref parameter cause it is filled in our example from Stack. The 
Publisher mixin HOST.


In other words. Where ever you mixin the Publisher template a message 
will be broad casted to all subscribers containing 2 information.
1 ) a value of type T, and an enum value INSERT, DELETE etc...

will read your comments tomorrow more carefully.. have to sleep now.)

bjoern
Jul 06 2010
prev sibling next sibling parent reply BLS <windevguy hotmail.de> writes:
Okay a  bit better snippet than before. snippet should be almost 
functional..

/*
Hi,
Andrei brings in the idea of std.pattern. Seems that this module is
"stalled";  Unfortunately !
However I would like to enhance collection classes (likewise
dcollections)  with a Publisher - Subscriber pattern (signal - slot, or
observer pattern) , if you prefer)
Hope the idea of enhancing collections with events become clear with the
following snippet.
!!! I am able to spend just a few hours a month with D programming.. in
other words, please don't kill me :)
Untested Draft code which requires a lot of help and ,more important,
feedback from you.

*/

struct publisherMsg(T) {
	T data;
	Action a;
}

mixin template Publisher(T) {
private :

	enum Action = {INSERT, UPDATE, DELETE, READONLY};	
	//alias typeof(this) PT;

	struct receiver {
		Object o;
		callback cb;	
	}

	receiver[] subscriber;
		
	publisherMsg!(T) msg;
	
	alias void delegate(const ref msg) callback;		
	
	void addSubscriber(object o, callback cb) {
		//subscriber ~= o;
	}	
	
	void publish() {
		foreach  (object o ; subscriber)
		{
		// create message and send message

		}
	}	
}

mixin template Subscriber() {
   // see UndoList for implementation	
}

final class Stack(T, bool observable = false ) {
	T[] data;	
	
	static if (observable)	mixin Observable!T;

	void push( T t) {
		data ~= t;
		publish(t, Action.INSERT);	
	}
	T pop() {
		publish(t, Action.DELETE);
		//...
	}	
	bool empty() {
		
	}
	T top() {
		publish(t, Action.READONLY);
		//...
	}
	size_t size() {

	}
}
// Undo list will receive every pushed or popped item -data and action)
class UndoList(T) {
	private:
	T[] data;
	
	/// should be part of the sunscriber mixin templates.
	publisherMsg!T stackMessage;

	void delegate(const ref stackMessage) dg;
	
	alias Stack!(int) IntegerStack;
	
	IntegerStack intstack = new IntegerStack;
		
	private this() {
		dg = &this.feedback;
		
		// SUBBSCRIBE Stack(T) push and pop events.
		intstack.addSubscriber(this, dg);
		
	}
	public void feedback(const ref stackMessage msg ) {
		writefln("Action");
	}
}
Jul 06 2010
next sibling parent "Rory McGuire" <rmcguire neonova.co.za> writes:
On Wed, 07 Jul 2010 00:47:32 +0200, BLS <windevguy hotmail.de> wrote:

 Okay a  bit better snippet than before. snippet should be almost  
 functional..

 /*
 Hi,
 Andrei brings in the idea of std.pattern. Seems that this module is
 "stalled";  Unfortunately !
 However I would like to enhance collection classes (likewise
 dcollections)  with a Publisher - Subscriber pattern (signal - slot, or
 observer pattern) , if you prefer)
 Hope the idea of enhancing collections with events become clear with the
 following snippet.
 !!! I am able to spend just a few hours a month with D programming.. in
 other words, please don't kill me :)
 Untested Draft code which requires a lot of help and ,more important,
 feedback from you.

 */

 struct publisherMsg(T) {
 	T data;
 	Action a;
 }

 mixin template Publisher(T) {
 private :

 	enum Action = {INSERT, UPDATE, DELETE, READONLY};	
 	//alias typeof(this) PT;

 	struct receiver {
 		Object o;
 		callback cb;	
 	}

 	receiver[] subscriber;
 		
 	publisherMsg!(T) msg;
 	
 	alias void delegate(const ref msg) callback;		
 	
 	void addSubscriber(object o, callback cb) {
 		//subscriber ~= o;
 	}	
 	
 	void publish() {
 		foreach  (object o ; subscriber)
 		{
 		// create message and send message

 		}
 	}	
 }

 mixin template Subscriber() {
    // see UndoList for implementation	
 }

 final class Stack(T, bool observable = false ) {
 	T[] data;	
 	
 	static if (observable)	mixin Observable!T;

 	void push( T t) {
 		data ~= t;
 		publish(t, Action.INSERT);	
 	}
 	T pop() {
 		publish(t, Action.DELETE);
 		//...
 	}	
 	bool empty() {
 		
 	}
 	T top() {
 		publish(t, Action.READONLY);
 		//...
 	}
 	size_t size() {

 	}
 }
 // Undo list will receive every pushed or popped item -data and action)
 class UndoList(T) {
 	private:
 	T[] data;
 	
 	/// should be part of the sunscriber mixin templates.
 	publisherMsg!T stackMessage;

 	void delegate(const ref stackMessage) dg;
 	
 	alias Stack!(int) IntegerStack;
 	
 	IntegerStack intstack = new IntegerStack;
 		
 	private this() {
 		dg = &this.feedback;
 		
 		// SUBBSCRIBE Stack(T) push and pop events.
 		intstack.addSubscriber(this, dg);
 		
 	}
 	public void feedback(const ref stackMessage msg ) {
 		writefln("Action");
 	}
 }

Hi Bjoern, your mixin template should have "private { ... }" rather than "private:" I believe the current way will make everything that uses the mixin template private after its used.
Jul 06 2010
prev sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 07.07.2010 2:47, BLS wrote:
 Okay a  bit better snippet than before. snippet should be almost 
 functional..

 /*
 Hi,
 Andrei brings in the idea of std.pattern. Seems that this module is
 "stalled";  Unfortunately !
 However I would like to enhance collection classes (likewise
 dcollections)  with a Publisher - Subscriber pattern (signal - slot, or
 observer pattern) , if you prefer)
 Hope the idea of enhancing collections with events become clear with the
 following snippet.
 !!! I am able to spend just a few hours a month with D programming.. in
 other words, please don't kill me :)
 Untested Draft code which requires a lot of help and ,more important,
 feedback from you.

 */

 struct publisherMsg(T) {
     T data;
     Action a;
 }

 mixin template Publisher(T) {
 private :

     enum Action = {INSERT, UPDATE, DELETE, READONLY};
     //alias typeof(this) PT;

     struct receiver {
         Object o;
         callback cb;
     }

     receiver[] subscriber;

     publisherMsg!(T) msg;

     alias void delegate(const ref msg) callback;

     void addSubscriber(object o, callback cb) {
         //subscriber ~= o;
     }

     void publish() {

         foreach  (object o ; subscriber)
         {
         // create message and send message

         }
     }

foreach(c;subscriber) c.callback( see my erlier post
 }

 mixin template Subscriber() {
   // see UndoList for implementation
 }

 final class Stack(T, bool observable = false ) {
     T[] data;

     static if (observable)    mixin Observable!T;

methods should be: addObserver instead of addSubscriber, notify instead of publish? But back to the whole idea - I'm sure you do not want to reimplement all the shiny std.container. What I think we basically need is universal wrapper for _any_ container stuff. let's call that Observable!Cont. Something like : final class Observable(Cont){ Cont cont; //here goes your current Publisher stuff //... alias Cont!ElementType T; static if(__traits(compiles,cont.insert(T.init)))//some such ... need to test if it has this method { size_t insert(Stuff)(Stuff s){//trouble is - we could insert anything that converts to T size_t n = cont.insert(s); notify(cast!(T)s,Action.Insert); } } //and all such stuff from std.continer.TotalContainer //in theory could even be generated from phobos source .... } usage : alias Observable!(Array!int) observableArray; observableArray arr = [ 4,5,6]; //that would be nice arr.addObserver( (ref msg m){ writeln("arr insertion: ",to!string(m.data,m.action)); arr.insert(42); //should print that fancy message ... Yes, I still think that plain delegates approach is more flexible and straightforward (see my earlier post).
     void push( T t) {
         data ~= t;
         publish(t, Action.INSERT);
     }
     T pop() {
         publish(t, Action.DELETE);
         //...
     }
     bool empty() {

     }
     T top() {
         publish(t, Action.READONLY);
         //...
     }
     size_t size() {

     }
 }
 // Undo list will receive every pushed or popped item -data and action)
 class UndoList(T) {
     private:
     T[] data;

     /// should be part of the sunscriber mixin templates.
     publisherMsg!T stackMessage;

     void delegate(const ref stackMessage) dg;

     alias Stack!(int) IntegerStack;

     IntegerStack intstack = new IntegerStack;

     private this() {
         dg = &this.feedback;

         // SUBBSCRIBE Stack(T) push and pop events.
         intstack.addSubscriber(this, dg);

intstack.addSubcriber( (const ref stackMessage msg){ feedback(msg); } ); //somewhat scary in this case, but in fact it does not restrict you to class instances.
     }
     public void feedback(const ref stackMessage msg ) {
         writefln("Action");
     }
 }

of functionality for my upcoming gui lib. It all just needs to be more reusable and flexible. -- Dmitry Olshansky
Jul 07 2010
parent reply Jacob Carlborg <doob me.com> writes:
On 2010-07-07 10.48, Dmitry Olshansky wrote:
 On 07.07.2010 2:47, BLS wrote:
 Okay a bit better snippet than before. snippet should be almost
 functional..

 /*
 Hi,
 Andrei brings in the idea of std.pattern. Seems that this module is
 "stalled"; Unfortunately !
 However I would like to enhance collection classes (likewise
 dcollections) with a Publisher - Subscriber pattern (signal - slot, or
 observer pattern) , if you prefer)
 Hope the idea of enhancing collections with events become clear with the
 following snippet.
 !!! I am able to spend just a few hours a month with D programming.. in
 other words, please don't kill me :)
 Untested Draft code which requires a lot of help and ,more important,
 feedback from you.

 */

 struct publisherMsg(T) {
 T data;
 Action a;
 }

 mixin template Publisher(T) {
 private :

 enum Action = {INSERT, UPDATE, DELETE, READONLY};
 //alias typeof(this) PT;

 struct receiver {
 Object o;
 callback cb;
 }

 receiver[] subscriber;

 publisherMsg!(T) msg;

 alias void delegate(const ref msg) callback;

 void addSubscriber(object o, callback cb) {
 //subscriber ~= o;
 }

 void publish() {

 foreach (object o ; subscriber)
 {
 // create message and send message

 }
 }

foreach(c;subscriber) c.callback( see my erlier post
 }

 mixin template Subscriber() {
 // see UndoList for implementation
 }

 final class Stack(T, bool observable = false ) {
 T[] data;

 static if (observable) mixin Observable!T;

methods should be: addObserver instead of addSubscriber, notify instead of publish? But back to the whole idea - I'm sure you do not want to reimplement all the shiny std.container. What I think we basically need is universal wrapper for _any_ container stuff. let's call that Observable!Cont. Something like : final class Observable(Cont){ Cont cont; //here goes your current Publisher stuff //... alias Cont!ElementType T; static if(__traits(compiles,cont.insert(T.init)))//some such ... need to test if it has this method { size_t insert(Stuff)(Stuff s){//trouble is - we could insert anything that converts to T size_t n = cont.insert(s); notify(cast!(T)s,Action.Insert); } } //and all such stuff from std.continer.TotalContainer //in theory could even be generated from phobos source .... } usage : alias Observable!(Array!int) observableArray; observableArray arr = [ 4,5,6]; //that would be nice arr.addObserver( (ref msg m){ writeln("arr insertion: ",to!string(m.data,m.action)); arr.insert(42); //should print that fancy message ... Yes, I still think that plain delegates approach is more flexible and straightforward (see my earlier post).
 void push( T t) {
 data ~= t;
 publish(t, Action.INSERT);
 }
 T pop() {
 publish(t, Action.DELETE);
 //...
 }
 bool empty() {

 }
 T top() {
 publish(t, Action.READONLY);
 //...
 }
 size_t size() {

 }
 }
 // Undo list will receive every pushed or popped item -data and action)
 class UndoList(T) {
 private:
 T[] data;

 /// should be part of the sunscriber mixin templates.
 publisherMsg!T stackMessage;

 void delegate(const ref stackMessage) dg;

 alias Stack!(int) IntegerStack;

 IntegerStack intstack = new IntegerStack;

 private this() {
 dg = &this.feedback;

 // SUBBSCRIBE Stack(T) push and pop events.
 intstack.addSubscriber(this, dg);

intstack.addSubcriber( (const ref stackMessage msg){ feedback(msg); } ); //somewhat scary in this case, but in fact it does not restrict you to class instances.
 }
 public void feedback(const ref stackMessage msg ) {
 writefln("Action");
 }
 }

of functionality for my upcoming gui lib. It all just needs to be more reusable and flexible.

I think it would be nice to simulate the events in C#. It's quite easy, just a struct containing a linked list of delegates, a function to call all the delegates and functions to add and remove delegates from the list. -- Jacob Carlborg
Jul 07 2010
parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 07.07.2010 14:54, Jacob Carlborg wrote:
 On 2010-07-07 10.48, Dmitry Olshansky wrote:
 On 07.07.2010 2:47, BLS wrote:
 Okay a bit better snippet than before. snippet should be almost
 functional..

 /*
 Hi,
 Andrei brings in the idea of std.pattern. Seems that this module is
 "stalled"; Unfortunately !
 However I would like to enhance collection classes (likewise
 dcollections) with a Publisher - Subscriber pattern (signal - slot, or
 observer pattern) , if you prefer)
 Hope the idea of enhancing collections with events become clear with 
 the
 following snippet.
 !!! I am able to spend just a few hours a month with D programming.. in
 other words, please don't kill me :)
 Untested Draft code which requires a lot of help and ,more important,
 feedback from you.

 */

 struct publisherMsg(T) {
 T data;
 Action a;
 }

 mixin template Publisher(T) {
 private :

 enum Action = {INSERT, UPDATE, DELETE, READONLY};
 //alias typeof(this) PT;

 struct receiver {
 Object o;
 callback cb;
 }

 receiver[] subscriber;

 publisherMsg!(T) msg;

 alias void delegate(const ref msg) callback;

 void addSubscriber(object o, callback cb) {
 //subscriber ~= o;
 }

 void publish() {

 foreach (object o ; subscriber)
 {
 // create message and send message

 }
 }

foreach(c;subscriber) c.callback( see my erlier post
 }

 mixin template Subscriber() {
 // see UndoList for implementation
 }

 final class Stack(T, bool observable = false ) {
 T[] data;

 static if (observable) mixin Observable!T;

methods should be: addObserver instead of addSubscriber, notify instead of publish? But back to the whole idea - I'm sure you do not want to reimplement all the shiny std.container. What I think we basically need is universal wrapper for _any_ container stuff. let's call that Observable!Cont. Something like : final class Observable(Cont){ Cont cont; //here goes your current Publisher stuff //... alias Cont!ElementType T; static if(__traits(compiles,cont.insert(T.init)))//some such ... need to test if it has this method { size_t insert(Stuff)(Stuff s){//trouble is - we could insert anything that converts to T size_t n = cont.insert(s); notify(cast!(T)s,Action.Insert); } } //and all such stuff from std.continer.TotalContainer //in theory could even be generated from phobos source .... } usage : alias Observable!(Array!int) observableArray; observableArray arr = [ 4,5,6]; //that would be nice arr.addObserver( (ref msg m){ writeln("arr insertion: ",to!string(m.data,m.action)); arr.insert(42); //should print that fancy message ... Yes, I still think that plain delegates approach is more flexible and straightforward (see my earlier post).
 void push( T t) {
 data ~= t;
 publish(t, Action.INSERT);
 }
 T pop() {
 publish(t, Action.DELETE);
 //...
 }
 bool empty() {

 }
 T top() {
 publish(t, Action.READONLY);
 //...
 }
 size_t size() {

 }
 }
 // Undo list will receive every pushed or popped item -data and action)
 class UndoList(T) {
 private:
 T[] data;

 /// should be part of the sunscriber mixin templates.
 publisherMsg!T stackMessage;

 void delegate(const ref stackMessage) dg;

 alias Stack!(int) IntegerStack;

 IntegerStack intstack = new IntegerStack;

 private this() {
 dg = &this.feedback;

 // SUBBSCRIBE Stack(T) push and pop events.
 intstack.addSubscriber(this, dg);

intstack.addSubcriber( (const ref stackMessage msg){ feedback(msg); } ); //somewhat scary in this case, but in fact it does not restrict you to class instances.
 }
 public void feedback(const ref stackMessage msg ) {
 writefln("Action");
 }
 }

of functionality for my upcoming gui lib. It all just needs to be more reusable and flexible.

I think it would be nice to simulate the events in C#. It's quite easy, just a struct containing a linked list of delegates, a function to call all the delegates and functions to add and remove delegates from the list.

in array instead of list, given the fact that number of callbacks is usually well under 100, it's should be faster and waste less space. I'll upload my efforts on dsource, when it's finally usable. -- Dmitry Olshansky
Jul 07 2010
prev sibling parent BLS <windevguy hotmail.de> writes:
  Dmitry
Thanks for all the feedback Dmitry. Seems that we have similar projects 
in mind :) (maybe we can talk about the GUI project.. mine is similar to 
win32++ at http://sourceforge.net/projects/win32-framework/)
------------------------------------------------------------------------

However : Like you I would prefer to use something like ..

void delegate(const ref message) CallBack;
CallBack[] cb;
 Jakob
Adding some delegates to our list (D dyn. array) like in C# is a piece 
of cake..

But keep in  mind that we also need  removeObserver() in order to 
implement the Observer pattern.  And removing makes the difference.

The question is now :
How do we identify our Observer object in order to remove..
possible solutions :

1) passing the object instance to the mixin template. (as shown in my 
code) adding of course a : toHash() method.
2)
adding a GUID to the delegate..
3)
...

Dmitry, I think

YOUR :

final class Observable(Cont){
     Cont cont;
     //here goes your current Publisher stuff
     //...
     alias Cont!ElementType T;
     static if(__traits(compiles,cont.insert(T.init)))//some such ... 	
need to test if it has this method

SOLUTION

is very hackish. (Beside, like IsForwardRange!R )

IMHO a mixin template is a more clean solution.

BUT this requires that container/collection classes are not final.

Well, even for final classes there is still the option to implement a 
decorator pattern! But the decorator pattern makes code quite unreadable...

However, ATM I am thinking about how we can you use std.concurrent 
message passing stuff...

Finally
Would be nice if we can write a few LOC together! ??
bjoern
PS> Sure the Publisher/Observable mixin has to work for structs too.
Jul 08 2010