www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to use labeled break in static foreach?

reply cc <cc nevernet.com> writes:
import std.meta;
enum A = AliasSeq!(1, 2, 3, 4);
THREELOOP: static foreach (idx, field; A) {
	static if (field == 3) {
		pragma(msg, "Got a 3!");
		break THREELOOP;
	}
	static if (idx == A.length - 1) {
		static assert(0, "Got no 3...");
	}
}

What I'd like to achieve in this example is for compilation to 
fail if the given tuple doesn't contain a matching item.  Is 
there an easy way to do this?  Trying to break out of the static 
foreach gives me
Error:enclosing label `THREELOOP` for `break` not found

In this reduced example, I was able to accomplish this by setting 
some enum within the successful comparison body and then checking 
e.g. static if (!__traits(compiles, FOUND)) but this breaks down 
once I start dealing with multiple levels of scope.
Feb 13 2020
next sibling parent cc <cc nevernet.com> writes:
Here's a more involved example of what I'm trying to accomplish.  
Is there an easier/cleaner way to do this?  (This is still a bit 
reduced, what I'm actually trying to do is compare whether a 
given variadic typetuple passed to opDispatch is implicitly 
convertible to one of the parameter definitions of overloaded 
functions with the same name on another object).

import std.meta;
struct Test { int[3] x; }
enum A = Test([1, 3, 3]);
enum B = Test([5, 6, 7]);
enum C = Test([5, 6, 7]);
enum D = Test([8, 9, 0]);
enum ARRS = AliasSeq!(A, B, C, D);

enum TESTER = Test([5, 6, 7]);

static foreach (ai, af; ARRS) {
	static if (!__traits(compiles, FOUND)) {
		static foreach (idx, field; af.x) {
			// Have to declare a different MISMATCH enum for each inner 
loop iteration
			//   unless we enclose it in its own {} scope, but if we do 
that then FOUND
			//   is not visible to the outer loop
			static if (!__traits(compiles, mixin(format("MISMATCH_%d", 
ai)))) {
				static if (TESTER.x[idx] != af.x[idx]) {
					mixin(`enum bool `~format("MISMATCH_%d", ai)~` = true;`);
				}
			}
			static if (idx == af.x.length - 1 && !__traits(compiles, 
mixin(format("MISMATCH_%d", ai)))) {
				enum FOUND = ai;
			}
		}
	}
}
static if (__traits(compiles, FOUND)) {

} else {
	static assert(0, "Got no match...");
}
Feb 13 2020
prev sibling next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
this kind of thing doesn't work super well due to the nature of 
compile time. My suggestion is to put the checks and the 
implementation in separate things.

void foo(T...)() {
     static bool checkHelper() {
         bool passed;
         static foreach(t; T) {
             static if(is(t == whatever)) {
                   passed = false;
             }
         }
         return passed;
     }

     static assert(checkHelper());

     // then foreach to do the rest of it
}


something like that. maybe returning string instead of bool to 
have an error message you print out with the static assert.

i don't love this but it is the simplest way I know.
Feb 14 2020
prev sibling next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/14/20 1:41 AM, cc wrote:
 import std.meta;
 enum A = AliasSeq!(1, 2, 3, 4);
 THREELOOP: static foreach (idx, field; A) {
      static if (field == 3) {
          pragma(msg, "Got a 3!");
          break THREELOOP;
      }
      static if (idx == A.length - 1) {
          static assert(0, "Got no 3...");
      }
 }
 
 What I'd like to achieve in this example is for compilation to fail if 
 the given tuple doesn't contain a matching item.  Is there an easy way 
 to do this?  Trying to break out of the static foreach gives me
 Error:enclosing label `THREELOOP` for `break` not found
 
 In this reduced example, I was able to accomplish this by setting some 
 enum within the successful comparison body and then checking e.g. static 
 if (!__traits(compiles, FOUND)) but this breaks down once I start 
 dealing with multiple levels of scope.
static foreach does not support break. Partly because the places where static foreach is allowed do not allow break statements. In your example above, if you are not in a function context, a break statement is not allowed. Note that a naked break statement inside a static foreach that's inside e.g. a switch statement needs a label to ensure the user understands they are breaking the switch, not the static foreach. foreach does allow breaks. If you use foreach on an AliasSeq, it will work. But again, you must be in a function context. But remember that static foreach and foreach implement all the bodies given. So every single one has to be compiled, even if it's not used. This can cause problems for things like return statements that make further code not executable. AND static foreach doesn't introduce a new scope (foreach does). Other than recursive templates, I can't think of a great way to do this. Your enum solution has limits, as you say. -Steve
Feb 14 2020
prev sibling parent reply visitor <pierredavant gmail.com> writes:
On Friday, 14 February 2020 at 06:41:02 UTC, cc wrote:

import std.meta;

enum A = AliasSeq!(1, 2, 3, 4);

static foreach (idx, field; A) {
     static if (__traits(compiles, THREELOOP)) {} else {
         static if (field == 3) {
		pragma(msg, "Got a 3!");
		enum THREELOOP = 1; // works with enum, alias etc...
	}
	static if (idx == A.length - 1) {
		static assert(0, "Got no 3...");
	}
     }
}


https://run.dlang.io/is/BDmIml
Sep 09 2020
parent reply visitor <pierredavant gmail.com> writes:
On Wednesday, 9 September 2020 at 17:02:26 UTC, visitor wrote:
 On Friday, 14 February 2020 at 06:41:02 UTC, cc wrote:

 import std.meta;

 enum A = AliasSeq!(1, 2, 3, 4);

 static foreach (idx, field; A) {
     static if (__traits(compiles, THREELOOP)) {} else {
         static if (field == 3) {
 		pragma(msg, "Got a 3!");
 		enum THREELOOP = 1; // works with enum, alias etc...
 	}
 	static if (idx == A.length - 1) {
 		static assert(0, "Got no 3...");
 	}
     }
 }


 https://run.dlang.io/is/BDmIml
oops the online editor saved a mix of an old iteration and a new one ... (probably my fault) import std.stdio; float test(float x) { float r = -1; static foreach(i; 0 .. 100) { static if (__traits(compiles, Check)) {} else { static if (i == 10) enum Check = 1; r = i * x; } } return r; } void main() { float ret = test(4.2); writefln("ret = %s", ret); }
Sep 09 2020
parent visitor <pierredavant gmail.com> writes:
https://run.dlang.io/is/xiqi4P

not pretty :)) but ...
Sep 09 2020