www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - [static] foreach scope, template declaration ?

reply "Mathias LANG" <geod24 gmail.com> writes:
I'm a bit puzzled with the following behavior:

----
import std.typetuple, std.traits;

struct UDAStruct {
     string identifier;
}

class MyClass {
      (UDAStruct("p1"), UDAStruct("p2"), UDAStruct("P3")) // P3 is 
a typo
       void func(int p1, string p2, float p3) {}
}

unittest {
     alias Func = MyClass.func;
     enum ParamNames = ParameterIdentifierTuple!Func;
     enum ParamAttr = __traits(getAttributes, Func);

     foreach (attr; ParamAttr) {
         template CmpName(string PName) {
             pragma(msg, "Instantiated for: "~PName);
             enum CmpName = (PName == attr.identifier);
         }
         pragma(msg, "Current attr is: "~attr.identifier);
         static assert(anySatisfy!(CmpName, ParamNames));
     }

     // Foreach does introduce a scope, as this produce no compile 
time error.
     template CmpName(string test) { enum CmpName = test; }
     static assert(CmpName!"?" == "?");
}

void main() {}
----

The output is (FE 2.066 & 2.065 tested):
148 geod24 barsoom2 ~ % dmd -unittest -run test.d
Current attr is: p1
Instantiated for: p1
Instantiated for: p2
Instantiated for: p3
Current attr is: p2
Current attr is: P3

Obviously one call tell it's not what I expected. It looks like 
DMD is reusing the instantiations of the template of the first 
loop for p2 and P3.
The 2 lines at the end check that foreach does introduce a scope, 
but it behaves differently than what we're use to.

Is there a way around this ?
I tried to move CmpName outside the loop, then declare `alias 
Cmp(string x) = CmpName(attr, x);` in the loop, but it doesn't 
help (I guess the same thing happens?).
Sep 25 2014
parent reply "SlomoTheBrave" <SlomoTheBrave nowhere.it> writes:
On Thursday, 25 September 2014 at 22:11:20 UTC, Mathias LANG 
wrote:
 I'm a bit puzzled with the following behavior:

 ----
 import std.typetuple, std.traits;

 struct UDAStruct {
     string identifier;
 }

 class MyClass {
      (UDAStruct("p1"), UDAStruct("p2"), UDAStruct("P3")) // P3 
 is a typo
       void func(int p1, string p2, float p3) {}
 }

 unittest {
     alias Func = MyClass.func;
     enum ParamNames = ParameterIdentifierTuple!Func;
     enum ParamAttr = __traits(getAttributes, Func);

     foreach (attr; ParamAttr) {
         template CmpName(string PName) {
             pragma(msg, "Instantiated for: "~PName);
             enum CmpName = (PName == attr.identifier);
         }
         pragma(msg, "Current attr is: "~attr.identifier);
         static assert(anySatisfy!(CmpName, ParamNames));
     }

     // Foreach does introduce a scope, as this produce no 
 compile time error.
     template CmpName(string test) { enum CmpName = test; }
     static assert(CmpName!"?" == "?");
 }

 void main() {}
 ----

 The output is (FE 2.066 & 2.065 tested):
 148 geod24 barsoom2 ~ % dmd -unittest -run test.d
 Current attr is: p1
 Instantiated for: p1
 Instantiated for: p2
 Instantiated for: p3
 Current attr is: p2
 Current attr is: P3

 Obviously one call tell it's not what I expected. It looks like 
 DMD is reusing the instantiations of the template of the first 
 loop for p2 and P3.
 The 2 lines at the end check that foreach does introduce a 
 scope, but it behaves differently than what we're use to.

 Is there a way around this ?
 I tried to move CmpName outside the loop, then declare `alias 
 Cmp(string x) = CmpName(attr, x);` in the loop, but it doesn't 
 help (I guess the same thing happens?).
a way around this is not to use anySatisfy nor the template, for example this works as expected: import std.typetuple, std.traits; struct UDAStruct { string identifier; } class MyClass { (UDAStruct("p1"), UDAStruct("p2"), UDAStruct("P3")) // P3 isa typo void func(int p1, string p2, float p3) {} } ------ void main(string[] args){ alias Func = MyClass.func; enum ParamNames = ParameterIdentifierTuple!Func; enum ParamAttr = __traits(getAttributes, Func); foreach(i,attr; ParamAttr) { static assert(attr.identifier == ParamNames[i], attr.identifier); } } ------ which is less abstruse. However I don't know if it has hurted your eyes too but the output lines order shows there is a problem too:
 Current attr is: p1
 Instantiated for: p1
 Instantiated for: p2
 Instantiated for: p3
 Current attr is: p2
 Current attr is: P3
instead of
 Current attr is: p1
 Instantiated for: p1
 Current attr is: p2
 Instantiated for: p2
 Current attr is: P3
 Instantiated for: p3
o!o
Sep 25 2014
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 09/25/2014 04:08 PM, SlomoTheBrave wrote:

 On Thursday, 25 September 2014 at 22:11:20 UTC, Mathias LANG wrote:
 I'm a bit puzzled with the following behavior:

 ----
 import std.typetuple, std.traits;

 struct UDAStruct {
     string identifier;
 }

 class MyClass {
      (UDAStruct("p1"), UDAStruct("p2"), UDAStruct("P3")) // P3 is a typo
       void func(int p1, string p2, float p3) {}
 }

 unittest {
     alias Func = MyClass.func;
     enum ParamNames = ParameterIdentifierTuple!Func;
     enum ParamAttr = __traits(getAttributes, Func);

     foreach (attr; ParamAttr) {
         template CmpName(string PName) {
             pragma(msg, "Instantiated for: "~PName);
             enum CmpName = (PName == attr.identifier);
         }
         pragma(msg, "Current attr is: "~attr.identifier);
         static assert(anySatisfy!(CmpName, ParamNames));
The first invocation of that line will instantiate CmpName with ParamNames, which happens to be a TypeTuple of "p1", "p2", and "p3".
 the output lines order shows there is a problem too:

 Current attr is: p1
 Instantiated for: p1
 Instantiated for: p2
 Instantiated for: p3
Surprisingly, that indicates that anySatisfy did instantiate CmpName with all three string values, meaning that perhaps we don't have shortcut behavior for 'bool' eponymous templates. Copying and instrumenting anySatisfy's implementation: template myAnySatisfy(alias F, T...) { pragma(msg, "myAnySatisfy with "~[ T ]); static if(T.length == 0) { enum myAnySatisfy = false; pragma(msg, "length == 0 "~myAnySatisfy); } else static if (T.length == 1) { enum myAnySatisfy = F!(T[0]); pragma(msg, "length == 1 "~myAnySatisfy); } else { enum myAnySatisfy = myAnySatisfy!(F, T[ 0 .. $/2]) || myAnySatisfy!(F, T[$/2 .. $ ]); pragma(msg, "else "~myAnySatisfy); } } When I use myAnySatisfy instead of anySatisfy, I see that I am right: The last expression above does not stop instantiating after "p1". In other words, even though myAnySatisfy!(F, T[$/2 .. $ ] is unnecessary (because the first part of || is already 'true'), it gets instantiated anyway. Current attr is: p1 ["myAnySatisfy with ", "p1", "p2", "p3"] ["myAnySatisfy with ", "p1"] Instantiated for: p1 length == 1  ["myAnySatisfy with ", "p2", "p3"] ["myAnySatisfy with ", "p2"] Instantiated for: p2 length == 1 ["myAnySatisfy with ", "p3"] Instantiated for: p3 length == 1 else else  Current attr is: p2 Current attr is: P3 This looks like an enhancement request. Ali
Sep 25 2014
parent "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Thursday, 25 September 2014 at 23:37:11 UTC, Ali Çehreli wrote:
 Surprisingly, that indicates that anySatisfy did instantiate 
 CmpName with all three string values, meaning that perhaps we 
 don't have shortcut behavior for 'bool' eponymous templates.
Yes, that's the case (not just for eponymous templates, but I can't remember the details). There was a discussion about it on digitalmars.D a few months ago.
Sep 26 2014
prev sibling parent reply "Mathias LANG" <no spam.please> writes:
On Thursday, 25 September 2014 at 23:08:53 UTC, SlomoTheBrave 
wrote:
 a way around this is not to use anySatisfy nor the template, 
 for example this works as expected:

 [...]
My problem is that in this example, attributes are in the same order as the parameter. But this come from a code generator, which takes class defined by the user, so I have to assume they might not be in the correct order :)
 which is less abstruse. However I don't know if it has hurted 
 your eyes too but the output lines order shows there is a 
 problem too:

 Current attr is: p1
 Instantiated for: p1
 Instantiated for: p2
 Instantiated for: p3
 Current attr is: p2
 Current attr is: P3
instead of
 Current attr is: p1
 Instantiated for: p1
 Current attr is: p2
 Instantiated for: p2
 Current attr is: P3
 Instantiated for: p3
o!o
On Thursday, 25 September 2014 at 23:37:11 UTC, Ali Çehreli wrote:
 Surprisingly, that indicates that anySatisfy did instantiate 
 CmpName with all three string values, meaning that perhaps we 
 don't have shortcut behavior for 'bool' eponymous templates.
This is documented (http://dlang.org/phobos/std_typetuple.html#.anySatisfy): Evaluation is *not* short-circuited if a true result is encountered; the template predicate must be instantiable with all the given items.
 When I use myAnySatisfy instead of anySatisfy, I see that I am 
 right: The last expression above does not stop instantiating 
 after "p1". In other words, even though myAnySatisfy!(F, T[$/2 
 ..  $ ] is unnecessary (because the first part of || is already 
 'true'), it gets instantiated anyway.

 [...]

 This looks like an enhancement request.

 Ali
I didn't consider this aspect, and there's definitely ground for optimization. However, let's say I replace the definition of MyClass with: ---- class MyClass { (UDAStruct("p3"), UDAStruct("P2"), UDAStruct("p1")) // P2 is a typo void func(int p1, string p2, float p3) {} } ---- In this case, anySatisfy is right to instantiate all 3 templates on the first iteration. Here's the result: ---- $ dmd -unittest -run bug.d Current attr is: p3 Instantiated for: p1 Instantiated for: p2 Instantiated for: p3 Current attr is: P2 Current attr is: p1 ---- There is no needless instantiation in this case, yet we still have the same behaviour (static assert not triggered).
Sep 26 2014
parent "SlomoTheBrave" <SlomoTheBrave nowhere.it> writes:
On Friday, 26 September 2014 at 11:16:01 UTC, Mathias LANG wrote:
 On Thursday, 25 September 2014 at 23:08:53 UTC, SlomoTheBrave 
 wrote:
 a way around this is not to use anySatisfy nor the template, 
 for example this works as expected:

 [...]
My problem is that in this example, attributes are in the same order as the parameter. But this come from a code generator, which takes class defined by the user, so I have to assume they might not be in the correct order :)
Your problem is that you absolutely want to do some idiomatic D2 with std, instead, using a home-cooked static function it would work fine, as demonstrated by my example (currently you use an eponyme template, so basically it's just a function). If your aim is to highlight a bug then fill a bug report.
Sep 26 2014