Thread overview
Struct initializer in UDA
Sep 26, 2020
realhet
Sep 26, 2020
Anonymouse
Sep 27, 2020
realhet
Sep 27, 2020
Anonymouse
Sep 28, 2020
realhet
September 26, 2020
Hi,

  struct S{int a, b, c=9, d, e, f;}

Is there a way or a trick to declare an UDA by using a nice struct initializer?

It would be nice to be able to use the form:

  @S{f:42} int a;  //or something similar to this.

instead of this longer and error-prone way:

  @S(0, 0, 0, 9, 0, 42) int a;
September 26, 2020
On Saturday, 26 September 2020 at 16:05:58 UTC, realhet wrote:
> Hi,
>
>   struct S{int a, b, c=9, d, e, f;}
>
> Is there a way or a trick to declare an UDA by using a nice struct initializer?
>
> It would be nice to be able to use the form:
>
>   @S{f:42} int a;  //or something similar to this.
>
> instead of this longer and error-prone way:
>
>   @S(0, 0, 0, 9, 0, 42) int a;

I don't think you can currently, no, but I'd be happy to be proven wrong.

The closest I can get is @(S.init.c(9).f(42)) with use of opDispatch, which is easier to read but still ugly.
September 27, 2020
On Saturday, 26 September 2020 at 17:13:17 UTC, Anonymouse wrote:
> On Saturday, 26 September 2020 at 16:05:58 UTC, realhet wrote:
> The closest I can get is @(S.init.c(9).f(42)) with use of opDispatch, which is easier to read but still ugly.

All I can get is that the
- an identifier of a member is stronger than the opDispatch. -> Error: function expected before (), not S(0, 0).c of type int
- and if I prefix it with '_' it ruins toString. -> Error: no property toString for type onlineapp.S


import std.stdio, std.range, std.algorithm, std.traits, std.meta, std.conv, std.string, std.uni, std.meta, std.functional, std.exception;

struct S{
    int a, b;

    auto opDispatch(string name, T)(T value)
    if(name.startsWith("_"))
    {
        mixin(name[1..$], "= value;");
        return this;
    }
}

void main(){
    S.init._a(5).writeln;
}


Now I'm more confused, as the compiler completely ignores the if(name.startsWith("_")) constraint o.O
September 27, 2020
On Sunday, 27 September 2020 at 10:17:39 UTC, realhet wrote:
> On Saturday, 26 September 2020 at 17:13:17 UTC, Anonymouse wrote:
>> On Saturday, 26 September 2020 at 16:05:58 UTC, realhet wrote:
>> The closest I can get is @(S.init.c(9).f(42)) with use of opDispatch, which is easier to read but still ugly.
>
> All I can get is that the
> - an identifier of a member is stronger than the opDispatch. -> Error: function expected before (), not S(0, 0).c of type int
> - and if I prefix it with '_' it ruins toString. -> Error: no property toString for type onlineapp.S
>
>
> import std.stdio, std.range, std.algorithm, std.traits, std.meta, std.conv, std.string, std.uni, std.meta, std.functional, std.exception;
>
> struct S{
>     int a, b;
>
>     auto opDispatch(string name, T)(T value)
>     if(name.startsWith("_"))
>     {
>         mixin(name[1..$], "= value;");
>         return this;
>     }
> }
>
> void main(){
>     S.init._a(5).writeln;
> }
>
>
> Now I'm more confused, as the compiler completely ignores the if(name.startsWith("_")) constraint o.O

It works if you specialise opDispatch to take an int parameter instead of a type T. It smells like a bug but I don't know enough to say.

I used two opDispatches to be able to avoid having to use _a and _b, and std.algorithm.comparison.among to constrain them.

struct S{
    private int _a, _b;

    auto opDispatch(string name)(int value)
    if (name.among("a", "b"))
    {
        mixin("_", name, "= value;");
        return this;
    }

    auto opDispatch(string name)()
    if (name.among("a", "b"))
    {
       	mixin("return _", name, ";");
    }
}

void main(){
    S.init.a(123).b(456).writeln;
    S().b(456).a(123).writeln;  // Alternative syntax, may not work if opCall is defined
}

It's brittle in that you have to update and sync the two among("a", "b") constraints every time you add or remove a field, but I can't seem to get the names by introspection without it endlessly recursing over opDispatch again.
September 28, 2020
On Sunday, 27 September 2020 at 11:59:49 UTC, Anonymouse wrote:
> On Sunday, 27 September 2020 at 10:17:39 UTC, realhet wrote:
>> On Saturday, 26 September 2020 at 17:13:17 UTC, Anonymouse wrote:
>>> On Saturday, 26 September 2020 at 16:05:58 UTC, realhet wrote:

That looks the closes to the python named parameters or the struct initializer.

For my use case this opDispatch trick seems to be more flexible than the named-parameters thing:

@(FieldProps().range(-360, 360).format("%.2f").caption("Turret rotation").unit("deg")) float alpha = 0;

for example if I use the name: "logRange" it can also set the isLogarithmic flag as a side effect to true inside the FieldProps struct. Just by choosing a slightly different name.

With this idealized format it would be not possible:
@FieldProps{ range: {-360, 360}, format:"%.2f", caption:"Turret rotation", unit:"deg"} float alpha = 0;

The more work inside the struct is not a problem, because I'm willing to use it from 1000 places. Also __traits(allMembers) can help.

Thank you!