www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How do I set a class member value by its name in a string?

reply Marc <jckj33 gmail.com> writes:
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
next sibling parent Benjamin Thaut <code benjamin-thaut.de> writes:
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:

 __traits(getMember, myClass, name) = value;
but since name is only know at runtime, I can't use __traits(). What's a workaround for 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.
Dec 27 2017
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
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:

 __traits(getMember, myClass, name) = value;
but since name is only know at runtime, I can't use __traits(). What's a workaround for 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
Dec 27 2017
parent reply Mengu <mengukagan gmail.com> writes:
On Wednesday, 27 December 2017 at 20:54:17 UTC, bitwise wrote:
 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:

 __traits(getMember, myClass, name) = value;
but since name is only know at runtime, I can't use __traits(). What's a workaround for 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
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
parent reply Mengu <mengukagan gmail.com> writes:
On Wednesday, 27 December 2017 at 21:39:49 UTC, Mengu wrote:
 On Wednesday, 27 December 2017 at 20:54:17 UTC, bitwise 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.
return type should just be void. that's just my muscle memory. :-D
Dec 27 2017
parent reply Biotronic <simen.kjaras gmail.com> writes:
On Wednesday, 27 December 2017 at 21:42:53 UTC, Mengu wrote:
 On Wednesday, 27 December 2017 at 21:39:49 UTC, Mengu wrote:
 On Wednesday, 27 December 2017 at 20:54:17 UTC, bitwise 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.
return type should just be void. that's just my muscle memory. :-D
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); } -- Biotronic
Dec 27 2017
parent reply Mengu <mengukagan gmail.com> writes:
On Wednesday, 27 December 2017 at 23:47:14 UTC, Biotronic wrote:
 [...]
much, much better. thanks biotronic.
Dec 28 2017
parent Marc <jckj33 gmail.com> writes:
Always helpful. Thank you very much guys.
Dec 28 2017