Thread overview
switch to member
Jan 14, 2017
Ignacious
Jan 14, 2017
Nicholas Wilson
Jan 14, 2017
Meta
Jan 14, 2017
Ignacious
Jan 14, 2017
Meta
Jan 14, 2017
Ali Çehreli
Jan 14, 2017
Marc Schütz
Jan 14, 2017
Ivan Kazmenko
Jan 14, 2017
Ignacious
Jan 14, 2017
Ivan Kazmenko
January 14, 2017
When doing common functionality for a switch, is there any way to optimize:

switch(x)
{
    case X:
         q.X = e;
         break;
    case Y:
         q.Y = e;
         break
    etc...
}

e is basically a value that, depending on the what kind(x), we assign it to a field in q. The name of the field and the case label are identical(in fact, it wouldn't hurt to do it either automatically(if x's enum name, if exists, matches a field, auto assign) or create the field in the class q automatically if the label matches a certain range or set).

Basically the idea is to avoid duplicating a lot of code.

I imagine one could write a string mixin that generates the cases and assignments but I'm hoping for a more elegant solution.
January 14, 2017
On Saturday, 14 January 2017 at 03:20:24 UTC, Ignacious wrote:
> When doing common functionality for a switch, is there any way to optimize:
>
> switch(x)
> {
>     case X:
>          q.X = e;
>          break;
>     case Y:
>          q.Y = e;
>          break
>     etc...
> }
>
> e is basically a value that, depending on the what kind(x), we assign it to a field in q. The name of the field and the case label are identical(in fact, it wouldn't hurt to do it either automatically(if x's enum name, if exists, matches a field, auto assign) or create the field in the class q automatically if the label matches a certain range or set).
>
> Basically the idea is to avoid duplicating a lot of code.
>
> I imagine one could write a string mixin that generates the cases and assignments but I'm hoping for a more elegant solution.

if `q` is the only data structure that  you are wanting to do this for
(or a 1:1 mapping for enum and type) then you can make
the enumeration values of x equal to the offsetof of q.
e.g. where typeof(q) == Q

enum XX
{
    X = Q.X.offsetof,
    Y = Q.Y.offsetof
    //ect.
}

and then

*(cast(void*)(this) + x) = e; //if inside struct/class

or
*(cast(void*)(q) + x) = e; // if outside

Unfortunately this loses you `@safe`ty, but as long as you trust
the value of x then it should be safe to `@trusted` that code.

If you are trying to avoid code duplication the enum declaration of X
could also be done with a string mixin.
January 13, 2017
On 01/13/2017 07:20 PM, Ignacious wrote:
> When doing common functionality for a switch, is there any way to optimize:
>
> switch(x)
> {
>     case X:
>          q.X = e;
>          break;
>     case Y:
>          q.Y = e;
>          break
>     etc...
> }
>
> e is basically a value that, depending on the what kind(x), we assign it
> to a field in q. The name of the field and the case label are
> identical(in fact, it wouldn't hurt to do it either automatically(if x's
> enum name, if exists, matches a field, auto assign) or create the field
> in the class q automatically if the label matches a certain range or set).
>
> Basically the idea is to avoid duplicating a lot of code.
>
> I imagine one could write a string mixin that generates the cases and
> assignments but I'm hoping for a more elegant solution.

Sounds like opDispatch may be useful.

Ali

January 14, 2017
On Saturday, 14 January 2017 at 05:29:49 UTC, Nicholas Wilson wrote:
> enum XX
> {
>     X = Q.X.offsetof,
>     Y = Q.Y.offsetof
>     //ect.
> }
>
> and then
>
> *(cast(void*)(this) + x) = e; //if inside struct/class
>
> or
> *(cast(void*)(q) + x) = e; // if outside
>
> Unfortunately this loses you `@safe`ty, but as long as you trust
> the value of x then it should be safe to `@trusted` that code.
>
> If you are trying to avoid code duplication the enum declaration of X
> could also be done with a string mixin.

IMO this is massive overkill to save some typing. To the OP, it's not really worth it to go to so much trouble. Just write some slightly duplicated code and move on.
January 14, 2017
You can utilize a little-known `switch` syntax trick in combination with `foreach`. Because a `foreach` over tuples is unrolled at compile time, it works even if your fields don't have exactly the same types:

--------------------------------------------------------------

struct Foo {
    int x, y;
    long a, b, c;
    short i, j, k;
}

enum Which {
    x, y, a, b, c, i, j, k,
}

void assignValue(ref Foo q, Which member, short e) {
    import std.traits : EnumMembers;
    import std.conv : to;

    final switch(member) {
        // foreach over a tuple is unrolled at compile time
        foreach(w; EnumMembers!Which) {
            case w:
                // expands to: q.x, q.y, ...
                mixin("q." ~ w.to!string) = e;
                break;
        }
    }
}

void main() {
    import std.stdio : writeln;
    Foo q;
    writeln("before: ", q);
    assignValue(q, Which.a, 42);
    assignValue(q, Which.x, 1);
    writeln("after: ", q);
}
January 14, 2017
On Saturday, 14 January 2017 at 03:20:24 UTC, Ignacious wrote:
> switch(x)
> {
>     case X:
>          q.X = e;
>          break;
>     case Y:
>          q.Y = e;
>          break
>     etc...
> }

Do you mean that verbatim?  Or are the case values strings, like:

switch(x)
{
    case "foo":
         q.foo = e;
         break;
    case "bar":
         q.bar = e;
         break
}

> I imagine one could write a string mixin that generates the cases and assignments but I'm hoping for a more elegant solution.

In any case, I also can imagine a mixin answer, but not much better.  Unless you want to actually look at the broader picture and maybe redesign the surrounding code to somehow cleverly get rid of the switch altogether.  The question as it is however doesn't give the context to make it possible.

Ivan Kazmenko.

January 14, 2017
On Saturday, 14 January 2017 at 11:32:10 UTC, Marc Schütz wrote:
> You can utilize a little-known `switch` syntax trick in combination with `foreach`. Because a `foreach` over tuples is unrolled at compile time, it works even if your fields don't have exactly the same types:
>
> <snip>

That looks concise.  Perhaps enum Which can also be automatically filled by __traits (allMembers) or std.traits.Fields if needed.

January 14, 2017
On Saturday, 14 January 2017 at 08:30:04 UTC, Meta wrote:
> On Saturday, 14 January 2017 at 05:29:49 UTC, Nicholas Wilson wrote:
>> enum XX
>> {
>>     X = Q.X.offsetof,
>>     Y = Q.Y.offsetof
>>     //ect.
>> }
>>
>> and then
>>
>> *(cast(void*)(this) + x) = e; //if inside struct/class
>>
>> or
>> *(cast(void*)(q) + x) = e; // if outside
>>
>> Unfortunately this loses you `@safe`ty, but as long as you trust
>> the value of x then it should be safe to `@trusted` that code.
>>
>> If you are trying to avoid code duplication the enum declaration of X
>> could also be done with a string mixin.
>
> IMO this is massive overkill to save some typing. To the OP, it's not really worth it to go to so much trouble. Just write some slightly duplicated code and move on.

Go join the Nazi Youth group, you OSS Sympathizer!
January 14, 2017
On Saturday, 14 January 2017 at 11:32:10 UTC, Marc Schütz wrote:
> You can utilize a little-known `switch` syntax trick in combination with `foreach`. Because a `foreach` over tuples is unrolled at compile time, it works even if your fields don't have exactly the same types:
>
> --------------------------------------------------------------
>
> struct Foo {
>     int x, y;
>     long a, b, c;
>     short i, j, k;
> }
>
> enum Which {
>     x, y, a, b, c, i, j, k,
> }
>
> void assignValue(ref Foo q, Which member, short e) {
>     import std.traits : EnumMembers;
>     import std.conv : to;
>
>     final switch(member) {
>         // foreach over a tuple is unrolled at compile time
>         foreach(w; EnumMembers!Which) {
>             case w:
>                 // expands to: q.x, q.y, ...
>                 mixin("q." ~ w.to!string) = e;
>                 break;
>         }
>     }
> }
>
> void main() {
>     import std.stdio : writeln;
>     Foo q;
>     writeln("before: ", q);
>     assignValue(q, Which.a, 42);
>     assignValue(q, Which.x, 1);
>     writeln("after: ", q);
> }

Cool, pretty straightforward and somewhat easy to use. I suppose it might be easier to mark the enum members with an attribute though and use that rather than having two enums? I didn't know about the foreach in the switch, cool idea!

Thanks.

January 14, 2017
On Saturday, 14 January 2017 at 16:05:33 UTC, Ignacious wrote:
> Go join the Nazi Youth group, you OSS Sympathizer!

What?