Thread overview
constructing labels for static foreach inside switch inside foreach
Jul 08, 2020
bauss
Jul 08, 2020
cc
Jul 08, 2020
Stanislav Blinov
Jul 08, 2020
ag0aep6g
July 07, 2020
OK, so I have a situation where I'm foreaching over a compile-time list of types. Inside the loop, I'm using a second loop over a set of input.

Inside that loop, I'm using a switch on the input, and inside the switch, I'm foreaching over the type's members, to construct a switch that can handle member names (this is for serialization).

If I encounter a certain name, then I want to break out of the inner loop (it's a while loop)

So naturally, I have to use break statements with labels like:

innerloop:
while(haveMoreData)
   switchstmt:
   switch(nextDataElement) {
      static foreach(name; __traits(allMembers, T)) {
      case name:
         ... // handle it
         break switchstmt;
      }
      case "STOP":
         break innerloop;
   }

Seems simple enough, except that this inner portion is unrolled, and if I have more than one type to run this on, I already have an "innerloop" label defined.

Is there a way to define a label using a mixin or something? or do I have to wrap this in a function?

Is there another way to approach this?

-Steve
July 08, 2020
On Wednesday, 8 July 2020 at 02:06:01 UTC, Steven Schveighoffer wrote:
> OK, so I have a situation where I'm foreaching over a compile-time list of types. Inside the loop, I'm using a second loop over a set of input.
>
> Inside that loop, I'm using a switch on the input, and inside the switch, I'm foreaching over the type's members, to construct a switch that can handle member names (this is for serialization).
>
> If I encounter a certain name, then I want to break out of the inner loop (it's a while loop)
>
> So naturally, I have to use break statements with labels like:
>
> innerloop:
> while(haveMoreData)
>    switchstmt:
>    switch(nextDataElement) {
>       static foreach(name; __traits(allMembers, T)) {
>       case name:
>          ... // handle it
>          break switchstmt;
>       }
>       case "STOP":
>          break innerloop;
>    }
>
> Seems simple enough, except that this inner portion is unrolled, and if I have more than one type to run this on, I already have an "innerloop" label defined.
>
> Is there a way to define a label using a mixin or something? or do I have to wrap this in a function?
>
> Is there another way to approach this?
>
> -Steve

Unfortunately mixin does not support labels so I don't think you can do what you want to do.

Kind of surprised me tbh
July 08, 2020
On Wednesday, 8 July 2020 at 02:06:01 UTC, Steven Schveighoffer wrote:
> OK, so I have a situation where I'm foreaching over a compile-time list of types. Inside the loop, I'm using a second loop over a set of input.
>
> Inside that loop, I'm using a switch on the input, and inside the switch, I'm foreaching over the type's members, to construct a switch that can handle member names (this is for serialization).
>
> If I encounter a certain name, then I want to break out of the inner loop (it's a while loop)
>
> So naturally, I have to use break statements with labels like:
>
> innerloop:
> while(haveMoreData)
>    switchstmt:
>    switch(nextDataElement) {
>       static foreach(name; __traits(allMembers, T)) {
>       case name:
>          ... // handle it
>          break switchstmt;
>       }
>       case "STOP":
>          break innerloop;
>    }
>
> Seems simple enough, except that this inner portion is unrolled, and if I have more than one type to run this on, I already have an "innerloop" label defined.
>
> Is there a way to define a label using a mixin or something? or do I have to wrap this in a function?
>
> Is there another way to approach this?
>
> -Steve

I think I ran into similar problems due to the requirement to use a labeled break inside static foreach.  I got around it by defining enums when my target was found and checking if it existed via __traits(compiles) to "ignore" the rest of the loop.
Sorry if I got what you're trying to accomplish wrong or this is too ugly:

class Foo {
	@(RPC) bar(int x, float f, string s) {
		// ...
	}
}

class Remoter(T) {
	void opDispatch(string s, SA...)(SA sargs) {
		alias A = getSymbolsByUDA!(T, RPC);
		static foreach (idx, FUNC; A) {
			static if (!__traits(compiles, FOUND) && hasUDA!(FUNC, RPC) && FUNCNAME!FUNC == s && SA.length == (Parameters!FUNC).length) {
				version(CheckImplicitlyConvertibleArgs) {
					static foreach (argi; 0 .. SA.length) {
						static if (!__traits(compiles, mixin(format("MISMATCH_%d", idx)))) {
							static if (isImplicitlyConvertible!(SA[argi], (Parameters!FUNC)[argi])) {
								//pragma(msg, format("implc ok: %s => %s", SA[argi].stringof, (Parameters!FUNC)[argi].stringof));
								// Parameter Ok
							} else {
								pragma(msg, format("RPC argument[%s] of %s is not implicitly convertible: %s => %s", argi, FUNCNAME!FUNC, SA[argi].stringof, (Parameters!FUNC)[argi].stringof));
								mixin(`enum bool `~format("MISMATCH_%d", idx)~` = true;`);
							}
						}
					}
					static if (!__traits(compiles, mixin(format("MISMATCH_%d", idx)))) {
						enum FOUND = idx;
						//pragma(msg, format("and we found: %s", FOUND));
					}
				} else {
					enum FOUND = idx;
				}
			}
		}
		static if (__traits(compiles, FOUND)) {
			alias FUNC = A[FOUND];

			// generate a packet to transmit that corresponds to RPC function call

		} else {
			static assert(0, format("No matching function found for %s%s", s, SA.stringof));
		}
	}
}

Remoter!foo remote;
remote.bar(4, 3.14f, "hello"); // succeeds
remote.bar("hi", 12); // static assert fail

July 08, 2020
On Wednesday, 8 July 2020 at 02:06:01 UTC, Steven Schveighoffer wrote:

> Seems simple enough, except that this inner portion is unrolled, and if I have more than one type to run this on, I already have an "innerloop" label defined.
>
> Is there a way to define a label using a mixin or something? or do I have to wrap this in a function?
>
> Is there another way to approach this?

Can't you put a single label after your outer foreach and goto it?
July 08, 2020
On 7/8/20 5:10 AM, cc wrote:
> 
> I think I ran into similar problems due to the requirement to use a labeled break inside static foreach.  I got around it by defining enums when my target was found and checking if it existed via __traits(compiles) to "ignore" the rest of the loop.

Thanks for the suggestion. I've used that trick too, but in this case, I still have a problem with the switch label as well (I need to use labeled breaks inside the static foreach that generates case clauses).

And in this case, the loop I'm breaking is NOT a static loop, it's a regular while loop. And I don't want to ignore the rest of the static loop.

-Steve
July 08, 2020
On 7/8/20 6:13 AM, Stanislav Blinov wrote:
> On Wednesday, 8 July 2020 at 02:06:01 UTC, Steven Schveighoffer wrote:
> 
>> Seems simple enough, except that this inner portion is unrolled, and if I have more than one type to run this on, I already have an "innerloop" label defined.
>>
>> Is there a way to define a label using a mixin or something? or do I have to wrap this in a function?
>>
>> Is there another way to approach this?
> 
> Can't you put a single label after your outer foreach and goto it?

There is potentially code after the while loop that needs to run, but I could move that code somewhere else. However it still won't work, because static foreach requires a label for break. So if I fix the loop problem, I will still have a label problem, because I need to label the switch and use labeled breaks on that.

This is really an issue with:

a) static foreach-ing case clauses requires a labeled break.
b) static foreach (or foreach on a tuple) unrolls all the code inside, including labels
c) labels have to be unique within the function.

I solved it for now by extrapolating the inner code into a local template function. But this is definitely an awkward situation for static foreach.

I was hoping I could use mixins or something to make new label names, but it appears I cannot (without mixing in the entire loop contents).

-Steve
July 08, 2020
On 08.07.20 14:24, Steven Schveighoffer wrote:
> I solved it for now by extrapolating the inner code into a local template function. But this is definitely an awkward situation for static foreach.

FWIW, you can write the extra function like this:

    static foreach (T; Types)
    () {
        innerloop: while (haveMoreData)
        {
            ...
            break innerloop;
            ...
        }
    } ();
July 08, 2020
On 7/8/20 9:38 AM, ag0aep6g wrote:
> On 08.07.20 14:24, Steven Schveighoffer wrote:
>> I solved it for now by extrapolating the inner code into a local template function. But this is definitely an awkward situation for static foreach.
> 
> FWIW, you can write the extra function like this:
> 
>      static foreach (T; Types)
>      () {
>          innerloop: while (haveMoreData)
>          {
>              ...
>              break innerloop;
>              ...
>          }
>      } ();


Ooh, that's a good idea. That avoids having to declare a template function before the loop (I can't declare it inside because, then multiple functions of the same name would exist lol).

Works like a charm, thanks!

-Steve