digitalmars.D.learn - Mix struct types in the same array
- tirithen (43/43) Dec 18 2019 I want to have a array/list/queue of instances of various struct
- Sebastiaan Koppe (3/10) Dec 18 2019 If you know all types up front you can use the Sumtype library on
- tirithen (8/10) Dec 18 2019 Thanks, it's a good starting point, the best would be if I only
- Paul Backus (50/59) Dec 18 2019 If all of the structs need to implement a particular method (or
- tirithen (4/8) Dec 19 2019 Thank you for the example that looks like a wonderful example,
- tirithen (14/31) Dec 19 2019 Is there a way I can make the example a bit more generic even by
- Paul Backus (21/57) Dec 19 2019 The reason this doesn't work is that interface methods are
- tirithen (4/16) Dec 20 2019 Thanks for a good explanation around virtual methods and
- Laeeth Isharc (16/27) Dec 20 2019 https://github.com/atilaneves/concepts
I want to have a array/list/queue of instances of various struct types that represent events. How can I achieve that? With classes I would have interfaces I guess, but I want to use structs if possible as they seem less complex (and faster?). Can I wrap each struct with in another struct called something like Entry that in turn can contain the event struct some how, or will that just move the problem? import std.stdio; struct User { string username; string email; string unverifiedEmail; string password; } struct UserCreate { string username; string email; void applyTo(ref User user) { user.username = this.username; user.unverifiedEmail = this.email; } } struct UserChangePassword { string password; void applyTo(ref User user) { user.password = this.password; } } // Not usable for structs interface Event { void applyTo(T)(ref T entity); } void main() { // How can I create an array with mixed structs? Event[] events; events ~= UserCreate("hej", "hej example.com"); events ~= UserChangePassword("hased1234"); User user; foreach (event; events) { event.applyTo(user); } writeln(user); }
Dec 18 2019
On Wednesday, 18 December 2019 at 22:00:20 UTC, tirithen wrote:I want to have a array/list/queue of instances of various struct types that represent events. How can I achieve that? With classes I would have interfaces I guess, but I want to use structs if possible as they seem less complex (and faster?). Can I wrap each struct with in another struct called something like Entry that in turn can contain the event struct some how, or will that just move the problem?If you know all types up front you can use the Sumtype library on code.dlang.org
Dec 18 2019
On Wednesday, 18 December 2019 at 22:11:10 UTC, Sebastiaan Koppe wrote:If you know all types up front you can use the Sumtype library on code.dlang.orgThanks, it's a good starting point, the best would be if I only needed to define that the struct would implement void applyTo(ref User user) so that could be run in the loop to update the User entity. But I'll try the Sumtype library, that takes me forward, thanks for the answer!
Dec 18 2019
On Wednesday, 18 December 2019 at 22:17:21 UTC, tirithen wrote:On Wednesday, 18 December 2019 at 22:11:10 UTC, Sebastiaan Koppe wrote:If all of the structs need to implement a particular method (or set of methods), you can use an interface and a templated adapter class to give them a common type. Here's a simple example: import std.stdio: writeln; struct User { string name; } interface Action { void applyTo(ref User); } class ActionAdapter(T) : Action { T payload; this(T payload) { this.payload = payload; } override void applyTo(ref User user) { payload.applyTo(user); } } Action action(T)(T payload) { return new ActionAdapter!T(payload); } struct SayHello { void applyTo(ref User user) { writeln("Hello, ", user.name, "."); } } struct SayGoodbye { void applyTo(ref User user) { writeln("Goodbye, ", user.name, "."); } } void main() { Action[] actions = [action(SayHello()), action(SayGoodbye())]; auto user = User("Joe Schmoe"); foreach (action; actions) action.applyTo(user); }If you know all types up front you can use the Sumtype library on code.dlang.orgThanks, it's a good starting point, the best would be if I only needed to define that the struct would implement void applyTo(ref User user) so that could be run in the loop to update the User entity.
Dec 18 2019
On Thursday, 19 December 2019 at 00:00:56 UTC, Paul Backus wrote:If all of the structs need to implement a particular method (or set of methods), you can use an interface and a templated adapter class to give them a common type. Here's a simple example:Thank you for the example that looks like a wonderful example, I'll try it out. I like that the structs then can be declared when the array is constructed.
Dec 19 2019
On Thursday, 19 December 2019 at 00:00:56 UTC, Paul Backus wrote:interface Action { void applyTo(ref User); <-- change to applyTo(T)(ref T) } class ActionAdapter(T) : Action { T payload; this(T payload) { this.payload = payload; } override void applyTo(ref User user) <-- change to applyTo(U)(ref U entity) { payload.applyTo(user); } }Is there a way I can make the example a bit more generic even by replacing the User type with a template. I tried: applyTo(ref User); <-- applyTo(T)(ref T) applyTo(ref User user) <-- applyTo(U)(ref U entity) It compiled but crashed with: Linking... /usr/bin/ld: .dub/build/application-debug-linux.posix-x86_64-dmd_2089-4CFAD9B3CA5E5532C5E06 8F9332C781/tryit.o: in function `_Dmain': .....tryit/source/app.d:55: undefined reference to `_D5plant5tryit3app5Event__T7applyToTSQBjQBgQBd4UserZQzMFKQvZv' collect2: fel: ld returnerade avslutningsstatus 1 I'm still a bit new to this language so need to practice templates more, but I find D overall nice.
Dec 19 2019
On Thursday, 19 December 2019 at 18:21:32 UTC, tirithen wrote:On Thursday, 19 December 2019 at 00:00:56 UTC, Paul Backus wrote:The reason this doesn't work is that interface methods are virtual, and virtual methods can't be templates. [1] If you want to have multiple kinds of entities, one option is to create an Entity interface and EntityAdapter(T) class using the same technique as above, and have your applyTo method accept an Entity as its argument. You can see an example of what that would look like at the following link: https://gist.github.com/pbackus/f61734804a627d379c7916f783ea7725#file-oop_version-d Another option is to forget about classes and interfaces and do everything with templates, using a library like sumtype [2]. Here's a link to an example of what that would look like: https://gist.github.com/pbackus/f61734804a627d379c7916f783ea7725#file-template_version-d Advantages of the interface + adapter version: + Can easily add new Actions and Entities, without changing existing code + Method dispatch is handled automatically by the compiler Advantages of sumtype + templates version: + No need to allocate adapter objects on the heap [1] https://dlang.org/spec/function.html#virtual-functions [2] https://code.dlang.org/packages/sumtypeinterface Action { void applyTo(ref User); <-- change to applyTo(T)(ref T) } class ActionAdapter(T) : Action { T payload; this(T payload) { this.payload = payload; } override void applyTo(ref User user) <-- change to applyTo(U)(ref U entity) { payload.applyTo(user); } }Is there a way I can make the example a bit more generic even by replacing the User type with a template. I tried: applyTo(ref User); <-- applyTo(T)(ref T) applyTo(ref User user) <-- applyTo(U)(ref U entity) It compiled but crashed with: Linking... /usr/bin/ld: .dub/build/application-debug-linux.posix-x86_64-dmd_2089-4CFAD9B3CA5E5532C5E06 8F9332C781/tryit.o: in function `_Dmain': .....tryit/source/app.d:55: undefined reference to `_D5plant5tryit3app5Event__T7applyToTSQBjQBgQBd4UserZQzMFKQvZv' collect2: fel: ld returnerade avslutningsstatus 1 I'm still a bit new to this language so need to practice templates more, but I find D overall nice.
Dec 19 2019
On Thursday, 19 December 2019 at 21:26:51 UTC, Paul Backus wrote:The reason this doesn't work is that interface methods are virtual, and virtual methods can't be templates. [1] If you want to have multiple kinds of entities, one option is to create an Entity interface and EntityAdapter(T) class using the same technique as above, and have your applyTo method accept an Entity as its argument. You can see an example of what that would look like at the following link: https://gist.github.com/pbackus/f61734804a627d379c7916f783ea7725#file-oop_version-d Another option is to forget about classes and interfaces and do everything with templates, using a library like sumtype [2]. Here's a link to an example of what that would look like: https://gist.github.com/pbackus/f61734804a627d379c7916f783ea7725#file-template_version-dThanks for a good explanation around virtual methods and templates as well as examples! Now I have plenty of good examples to play around with. :)
Dec 20 2019
On Wednesday, 18 December 2019 at 22:17:21 UTC, tirithen wrote:On Wednesday, 18 December 2019 at 22:11:10 UTC, Sebastiaan Koppe wrote:https://github.com/atilaneves/concepts interface IFoo { int foo(int i, string s) safe; double lefoo(string s) safe; } implements!(Foo, IFoo) struct Foo { int foo(int i, string s) safe { return 0; } double lefoo(string s) safe { return 0; } } // doesn't compile /* implements!(Oops, IFoo) struct Oops {} */If you know all types up front you can use the Sumtype library on code.dlang.orgThanks, it's a good starting point, the best would be if I only needed to define that the struct would implement void applyTo(ref User user) so that could be run in the loop to update the User entity. But I'll try the Sumtype library, that takes me forward, thanks for the answer!
Dec 20 2019