www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Improve a simple event handler

reply JN <666total wp.pl> writes:
I am writing a simple event handler object for observer pattern.

https://gist.github.com/run-dlang/d58d084752a1f65148b33c796535a4e2

(note: the final implementation will use an array of listeners, 
but I want to keep it simple for now and have only one handler 
per event).

Is there some way I could improve this with some D features? My 
main gripes with it are:

1) generic typing. I wish event handler methods such as onResize 
and onWindowTitle could take concrete types like WindowResize and 
WindowTitle rather than the generic Event struct.

2) same when emitting the event. It feels a bit verbose.

I don't want to use classes or polymorphism. I was thinking 
perhaps std.variant could be useful here, although I am not 
exactly sure how it would work in practice.
Jan 15 2022
next sibling parent reply JN <666total wp.pl> writes:
On Saturday, 15 January 2022 at 23:15:16 UTC, JN wrote:
 Is there some way I could improve this with some D features? My 
 main gripes with it are:
Managed to dramatically simplify it to 10 lines of code with variadic templates. ```d import std.stdio; struct Event(T...) { void function(T)[] listeners; void addListener(void function(T) handler) { listeners ~= handler; } void emit(T args) { foreach (listener; listeners) { listener(args); } } } void onResize(uint newWidth, uint newHeight) { writefln("Resized: %d %d", newWidth, newHeight); } void main() { Event!(uint, uint) windowResizeEvent; windowResizeEvent.addListener(&onResize); windowResizeEvent.emit(1000, 2000); } ``` I am very happy with this solution.
Jan 16 2022
next sibling parent Sebastiaan Koppe <mail skoppe.eu> writes:
On Sunday, 16 January 2022 at 20:01:09 UTC, JN wrote:
 On Saturday, 15 January 2022 at 23:15:16 UTC, JN wrote:
 Is there some way I could improve this with some D features? 
 My main gripes with it are:
Managed to dramatically simplify it to 10 lines of code with variadic templates. ```d import std.stdio; struct Event(T...) { void function(T)[] listeners; void addListener(void function(T) handler) { listeners ~= handler; } void emit(T args) { foreach (listener; listeners) { listener(args); } } } void onResize(uint newWidth, uint newHeight) { writefln("Resized: %d %d", newWidth, newHeight); } void main() { Event!(uint, uint) windowResizeEvent; windowResizeEvent.addListener(&onResize); windowResizeEvent.emit(1000, 2000); } ``` I am very happy with this solution.
Looks good. But do note that with larger applications it inevitably becomes a ball of spaghetti, making it hard to understand why a certain widget behaves the way it does (or doesn't). The other problem - already apparent in this small example - is the absence of `removeListener`. It is a very crucial and often overlooked part that often only gets written afterwards. The problem is that it ties into lifetime which is hard to bolt on. For small things though, it works wonderfully.
Jan 17 2022
prev sibling parent Hipreme <msnmancini hotmail.com> writes:
On Sunday, 16 January 2022 at 20:01:09 UTC, JN wrote:
 On Saturday, 15 January 2022 at 23:15:16 UTC, JN wrote:
 Is there some way I could improve this with some D features? 
 My main gripes with it are:
Managed to dramatically simplify it to 10 lines of code with variadic templates. ```d import std.stdio; struct Event(T...) { void function(T)[] listeners; void addListener(void function(T) handler) { listeners ~= handler; } void emit(T args) { foreach (listener; listeners) { listener(args); } } } void onResize(uint newWidth, uint newHeight) { writefln("Resized: %d %d", newWidth, newHeight); } void main() { Event!(uint, uint) windowResizeEvent; windowResizeEvent.addListener(&onResize); windowResizeEvent.emit(1000, 2000); } ``` I am very happy with this solution.
My advice is to make your listeners return a boolean. This boolean is used to basically stop propagating the event. And debugging it seems really hard to be honest, so, incrementing it a little, I would do: ```d struct ListenerInfo { string name; string file; uint line; string registeredAt; } struct Event(T...) { bool function(T)[] listeners; ListenerInfo[] info; void addListener(bool function(T) handler, string name, string file = __FILE__, uint line = __LINE__, string registeredAt = __PRETTY_FUNCTION__) { listeners~= handler; info~= ListenerInfo(name, file, line, registeredAt); } void emit(T args) { foreach(i, l; listeners) { if(l(args)) { writeln(info[i]); break; } } } } ``` You could even extend this concept further by making it return an enum value for actually removing the listener when executed, stopping propagation. Or you could have access to the listener info inside the arguments too.
Jan 19 2022
prev sibling parent Salih Dincer <salihdb hotmail.com> writes:
On Saturday, 15 January 2022 at 23:15:16 UTC, JN wrote:
 I am writing a simple event handler object for observer pattern.

 https://gist.github.com/run-dlang/d58d084752a1f65148b33c796535a4e2

 (note: the final implementation will use an array of listeners,
Did you especially make an effort not to use money DI (Dependency injection)? Your codes will be more delicious if you implement DI. You can find its simple implementation [here.](https://forum.dlang.org/post/roqnezzxcuorscnmanhh forum.dlang.org) If you get the ConnectionSetup working, you can setup without knowing the service parameters. Salih
Jan 17 2022