digitalmars.D.learn - std.pattern.. templated publisher subscriber pattern, adding events
- BLS (84/84) Jul 06 2010 Hi,
- Dmitry Olshansky (18/91) Jul 06 2010 Why not just use plain delegate instead of pair Object,callback?
- BLS (9/9) Jul 06 2010 Hi Dimitry, thanks for the feedback!
- BLS (94/94) Jul 06 2010 Okay a bit better snippet than before. snippet should be almost
- Rory McGuire (5/99) Jul 06 2010 Hi Bjoern,
- Dmitry Olshansky (53/135) Jul 07 2010 The considerations are still the same :
- Jacob Carlborg (6/164) Jul 07 2010 I think it would be nice to simulate the events in C#. It's quite easy,
- Dmitry Olshansky (7/172) Jul 07 2010 Never actually used C#, but I'm doing something alike, just with built
- BLS (43/43) Jul 08 2010 @ Dmitry
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
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.Will do, probably both :)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() ? }Why not just use plain delegate instead of pair Object,callback? 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; }ditto.void publish(T t, Action act) { foreach (object o ; subscriber) { // create and send message } } }and then maybe just call all delegates with msg(t,act)?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
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
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
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
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() {Now where are arguments? kind of (T t,Action act)foreach (object o ; subscriber) { // create message and send message } }The considerations are still the same : 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;Yes, I think Observable is more appropriate then Publisher. Then maybe 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);Again from my POV it could be: 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"); } }BTW I'm definitely interesed in this effort, and in fact want that kind of functionality for my upcoming gui lib. It all just needs to be more reusable and flexible. -- Dmitry Olshansky
Jul 07 2010
On 2010-07-07 10.48, Dmitry Olshansky wrote:On 07.07.2010 2:47, BLS wrote: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 CarlborgOkay 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() {Now where are arguments? kind of (T t,Action act)foreach (object o ; subscriber) { // create message and send message } }The considerations are still the same : 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;Yes, I think Observable is more appropriate then Publisher. Then maybe 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);Again from my POV it could be: 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"); } }BTW I'm definitely interesed in this effort, and in fact want that kind of functionality for my upcoming gui lib. It all just needs to be more reusable and flexible.
Jul 07 2010
On 07.07.2010 14:54, Jacob Carlborg wrote:On 2010-07-07 10.48, Dmitry Olshansky wrote: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 OlshanskyOn 07.07.2010 2:47, BLS wrote: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.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() {Now where are arguments? kind of (T t,Action act)foreach (object o ; subscriber) { // create message and send message } }The considerations are still the same : 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;Yes, I think Observable is more appropriate then Publisher. Then maybe 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);Again from my POV it could be: 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"); } }BTW I'm definitely interesed in this effort, and in fact want that kind of functionality for my upcoming gui lib. It all just needs to be more reusable and flexible.
Jul 07 2010
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 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