Thread overview
How do I set a class member value by its name in a string?
Dec 27
Marc
Dec 27
bitwise
Dec 27
Mengu
Dec 27
Mengu
Dec 27
Biotronic
Dec 28
Mengu
Dec 28
Marc
December 27
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
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
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
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
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
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
On Wednesday, 27 December 2017 at 23:47:14 UTC, Biotronic wrote:
> [...]

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