digitalmars.D.learn - How do I set a class member value by its name in a string?
- Marc (4/5) Dec 27 2017 but since name is only know at runtime, I can't use __traits().
- Benjamin Thaut (24/29) Dec 27 2017 You will have to use a combination of compile time and runtime
- bitwise (7/12) Dec 27 2017 I think you could write something using a combination of these
- Mengu (26/40) Dec 27 2017 there's also a simple workaround for fields with the same type:
I'd like to set the members of a class by its name at runtime, I would do something like this:__traits(getMember, myClass, name) = value;but since name is only know at runtime, I can't use __traits(). What's a workaround for this?
Dec 27 2017
On Wednesday, 27 December 2017 at 20:04:29 UTC, Marc wrote:I'd like to set the members of a class by its name at runtime, I would do something like this:You will have to use a combination of compile time and runtime methologies. Essentially what you want is this: void setMemberValue(string name, int value) { switch(name) { case "member1": member1 = value; break; case "member2": member2 = value; break: ... } } As you don't want to write this for all members by hand you should write a function which generates the source code for this switch using static foreach and __traits(allMembers) and then mixin the generated string into the setMemberValue method. If your members can have different types you will also need runtime type that can hold multiple types. For simplicity I just used "int" in the above example. You could use "std.variant" for this.__traits(getMember, myClass, name) = value;but since name is only know at runtime, I can't use __traits(). What's a workaround for this?
Dec 27 2017
On Wednesday, 27 December 2017 at 20:04:29 UTC, Marc wrote:I'd like to set the members of a class by its name at runtime, I would do something like this:I think you could write something using a combination of these two things: https://dlang.org/phobos/std_traits.html#FieldNameTuple https://dlang.org/phobos/std_traits.html#Fields or maybe '.tupleof': https://dlang.org/spec/struct.html#struct_properties__traits(getMember, myClass, name) = value;but since name is only know at runtime, I can't use __traits(). What's a workaround for this?
Dec 27 2017
On Wednesday, 27 December 2017 at 20:54:17 UTC, bitwise wrote:On Wednesday, 27 December 2017 at 20:04:29 UTC, Marc wrote:there's also a simple workaround for fields with the same type: https://run.dlang.io/is/dsFajq import std.stdio; struct S { int x; int y; } auto setValue(ref S s, string field, int value) { foreach (fieldName; __traits(allMembers, S)) { if (fieldName == field) { __traits(getMember, s, fieldName) = value; break; } } } void main() { S s; s.setValue("x", 5); s.setValue("y", 25); writeln(s); } you can play with it to make it more generic. you can also create a mixin template that would generate setters for each field you would need a setter for and then in the run time you'd just be able to call them.I'd like to set the members of a class by its name at runtime, I would do something like this:I think you could write something using a combination of these two things: https://dlang.org/phobos/std_traits.html#FieldNameTuple https://dlang.org/phobos/std_traits.html#Fields or maybe '.tupleof': https://dlang.org/spec/struct.html#struct_properties__traits(getMember, myClass, name) = value;but since name is only know at runtime, I can't use __traits(). What's a workaround for this?
Dec 27 2017
On Wednesday, 27 December 2017 at 21:39:49 UTC, Mengu wrote:On Wednesday, 27 December 2017 at 20:54:17 UTC, bitwise wrote:return type should just be void. that's just my muscle memory. :-D[...]there's also a simple workaround for fields with the same type: https://run.dlang.io/is/dsFajq import std.stdio; struct S { int x; int y; } auto setValue(ref S s, string field, int value) { foreach (fieldName; __traits(allMembers, S)) { if (fieldName == field) { __traits(getMember, s, fieldName) = value; break; } } } void main() { S s; s.setValue("x", 5); s.setValue("y", 25); writeln(s); } you can play with it to make it more generic. you can also create a mixin template that would generate setters for each field you would need a setter for and then in the run time you'd just be able to call them.
Dec 27 2017
On Wednesday, 27 December 2017 at 21:42:53 UTC, Mengu wrote:On Wednesday, 27 December 2017 at 21:39:49 UTC, Mengu wrote:More generic, for more better: void setValue(T, V)(auto ref T aggregate, string field, V value) { import std.traits : FieldNameTuple; import std.meta : Alias; switch (field) { foreach (fieldName; FieldNameTuple!T) { case fieldName: static if (is(typeof(__traits(getMember, aggregate, fieldName) = value))) { __traits(getMember, aggregate, fieldName) = value; return; } else assert(false, T.stringof ~ "."~field~" cannot be assigned from a "~V.stringof~"."); } default: assert(false, T.stringof ~ " has no field named "~field~"."); } } unittest { import std.exception : assertThrown; import core.exception : AssertError; static struct S { int x; string s; } S s; s.setValue("x", 14); assert(s.x == 14); assertThrown!AssertError(s.setValue("q", 14)); assertThrown!AssertError(s.setValue("s", 14)); s.setValue("s", "abc123"); assert(s.s == "abc123"); } unittest { import std.exception : assertThrown; import core.exception : AssertError; static class C { int x; string s; } C c = new C; c.setValue("x", 14); assert(c.x == 14); assertThrown!AssertError(c.setValue("q", 14)); assertThrown!AssertError(c.setValue("s", 14)); c.setValue("s", "abc123"); assert(c.s == "abc123"); (new C).setValue("x", 143); } -- BiotronicOn Wednesday, 27 December 2017 at 20:54:17 UTC, bitwise wrote:return type should just be void. that's just my muscle memory. :-D[...]there's also a simple workaround for fields with the same type: https://run.dlang.io/is/dsFajq import std.stdio; struct S { int x; int y; } auto setValue(ref S s, string field, int value) { foreach (fieldName; __traits(allMembers, S)) { if (fieldName == field) { __traits(getMember, s, fieldName) = value; break; } } } void main() { S s; s.setValue("x", 5); s.setValue("y", 25); writeln(s); } you can play with it to make it more generic. you can also create a mixin template that would generate setters for each field you would need a setter for and then in the run time you'd just be able to call them.
Dec 27 2017
On Wednesday, 27 December 2017 at 23:47:14 UTC, Biotronic wrote:[...]much, much better. thanks biotronic.
Dec 28 2017