digitalmars.D.learn - Improve a simple event handler
- JN (14/14) Jan 15 2022 I am writing a simple event handler object for observer pattern.
- JN (32/34) Jan 16 2022 Managed to dramatically simplify it to 10 lines of code with
- Sebastiaan Koppe (10/46) Jan 17 2022 Looks good. But do note that with larger applications it
- Hipreme (41/77) Jan 19 2022 My advice is to make your listeners return a boolean. This
- Salih Dincer (8/11) Jan 17 2022 Did you especially make an effort not to use money DI (Dependency
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
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
On Sunday, 16 January 2022 at 20:01:09 UTC, JN wrote:On Saturday, 15 January 2022 at 23:15:16 UTC, JN wrote: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.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 17 2022
On Sunday, 16 January 2022 at 20:01:09 UTC, JN wrote:On Saturday, 15 January 2022 at 23:15:16 UTC, JN wrote: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.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 19 2022
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