www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - switch to member

reply Ignacious <I.D.T ProjectMaya.com> writes:
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.
Jan 13
next sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
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.
Jan 13
parent reply Meta <jared771 gmail.com> writes:
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.
Jan 14
parent reply Ignacious <I.D.T ProjectMaya.com> writes:
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!
Jan 14
parent Meta <jared771 gmail.com> writes:
On Saturday, 14 January 2017 at 16:05:33 UTC, Ignacious wrote:
 Go join the Nazi Youth group, you OSS Sympathizer!
What?
Jan 14
prev sibling next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
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
Jan 13
prev sibling next sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
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);
}
Jan 14
next sibling parent Ivan Kazmenko <gassa mail.ru> writes:
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.
Jan 14
prev sibling parent Ignacious <I.D.T ProjectMaya.com> writes:
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.
Jan 14
prev sibling parent Ivan Kazmenko <gassa mail.ru> writes:
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.
Jan 14