Thread overview
How do I set a class member value by its name in a string?
Dec 27, 2017
Marc
Dec 27, 2017
Benjamin Thaut
Dec 27, 2017
bitwise
Dec 27, 2017
Mengu
Dec 27, 2017
Mengu
Dec 27, 2017
Biotronic
Dec 28, 2017
Mengu
Dec 28, 2017
Marc
December 27, 2017
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?
December 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:
>
>> __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.
December 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:
>
>> __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



December 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:
>> 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.
December 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:
>> [...]
>
> 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
December 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:
>> 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
December 28, 2017
On Wednesday, 27 December 2017 at 23:47:14 UTC, Biotronic wrote:
> [...]

much, much better. thanks biotronic.
December 28, 2017
Always helpful. Thank you very much guys.