www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - static foreach over enum symbols

reply Ben Jones <fake fake.fake> writes:
Hi all,

I'm getting unexpected results while trying to process symbols 
from a module, some of which are enums.

Depending on whether or not I comment out the first static 
foreach loop below, fullyQualifiedName gives me different results 
in the second loop.

In either case, I'm surprised I can't grab the UDAs in the second 
static foreach loop.  Any ideas what's going on?

Test case:
---a.d---
module a;

 Object
enum x = "hello";

 Object
enum y = "goodbye";

 Object
struct z{}


---main.d---

template symbols(alias Mod){
     import std.meta;
     alias toSymbol(alias T) = __traits(getMember, Mod, T);
     alias symbols = staticMap!(toSymbol, __traits(allMembers, 
Mod));
}


void main(){
     import std.traits;
     import std.meta;
     import a;
     //commenting this out changes the results below
     static foreach(sym; symbols!a){
         pragma(msg, fullyQualifiedName!sym);
         pragma(msg, __traits(getAttributes, sym));
     }

     pragma(msg, "\nget with UDAs\n");
     pragma(msg, getSymbolsByUDA!(a, Object));
     alias udaSyms = getSymbolsByUDA!(a, Object);
     pragma(msg, staticMap!(fullyQualifiedName, udaSyms));
     static foreach(us; udaSyms){
         pragma(msg, fullyQualifiedName!us);
         pragma(msg, __traits(getAttributes, us));
     }
}

---

annotated output of dmd main.d:

with the first loop commented out:

get with UDAs

tuple("hello", "goodbye", (z))
tuple("a.x", "a.y", "a.z")
a.x
tuple()  //why is the UDA gone?
a.y
tuple()
a.z
tuple((Object))


and with the first loop:

object
tuple()
main.main.sym  //it's not a.x anymore, it's the name of the local 
var for static foreach?
tuple()
main.main.sym
tuple()
a.z
tuple((Object))

get with UDAs

tuple("hello", "goodbye", (z))
tuple("main.main.sym", "main.main.sym", "a.z") //and the results 
are changed here too?
main.main.sym
tuple()
main.main.sym
tuple()
a.z
tuple((Object))
Feb 14 2020
next sibling parent Boris Carvajal <boris2.9 gmail.com> writes:
On Friday, 14 February 2020 at 22:24:14 UTC, Ben Jones wrote:
 Hi all,

 I'm getting unexpected results while trying to process symbols 
 from a module, some of which are enums.

 Depending on whether or not I comment out the first static 
 foreach loop below, fullyQualifiedName gives me different 
 results in the second loop.

 In either case, I'm surprised I can't grab the UDAs in the 
 second static foreach loop.  Any ideas what's going on?

 Test case:
 ---a.d---
 module a;

  Object
 enum x = "hello";

  Object
 enum y = "goodbye";

  Object
 struct z{}


 ---main.d---

 template symbols(alias Mod){
     import std.meta;
     alias toSymbol(alias T) = __traits(getMember, Mod, T);
     alias symbols = staticMap!(toSymbol, __traits(allMembers, 
 Mod));
 }


 void main(){
     import std.traits;
     import std.meta;
     import a;
     //commenting this out changes the results below
     static foreach(sym; symbols!a){
         pragma(msg, fullyQualifiedName!sym);
         pragma(msg, __traits(getAttributes, sym));
     }

     pragma(msg, "\nget with UDAs\n");
     pragma(msg, getSymbolsByUDA!(a, Object));
     alias udaSyms = getSymbolsByUDA!(a, Object);
     pragma(msg, staticMap!(fullyQualifiedName, udaSyms));
     static foreach(us; udaSyms){
         pragma(msg, fullyQualifiedName!us);
         pragma(msg, __traits(getAttributes, us));
     }
 }

 ---

 annotated output of dmd main.d:

 with the first loop commented out:

 get with UDAs

 tuple("hello", "goodbye", (z))
 tuple("a.x", "a.y", "a.z")
 a.x
 tuple()  //why is the UDA gone?
 a.y
 tuple()
 a.z
 tuple((Object))


 and with the first loop:

 object
 tuple()
 main.main.sym  //it's not a.x anymore, it's the name of the 
 local var for static foreach?
 tuple()
 main.main.sym
 tuple()
 a.z
 tuple((Object))

 get with UDAs

 tuple("hello", "goodbye", (z))
 tuple("main.main.sym", "main.main.sym", "a.z") //and the 
 results are changed here too?
 main.main.sym
 tuple()
 main.main.sym
 tuple()
 a.z
 tuple((Object))
From language spec [1]: "the name of the static foreach variable is bound to the i-th entry of the sequence, either as an enum variable declaration (for constants) or an alias declaration (for symbols). Enums are treated differently, so your 'sym' is not an alias to the original enum but an enum itself with the same value. Because of that, fullyQualifiedName or any template with alias argument will just get the sym temporary not the enum from the symbols tuple. This code should work: static foreach(i, _; symbols!a){ pragma(msg, fullyQualifiedName!(symbols!a[i])); // or manually aliased, but you need to use regular foreach or {{ }} to avoid redefinitions. alias sym = symbols!a[i]; pragma(msg, fullyQualifiedName!sym); } [1] https://dlang.org/spec/version.html#staticforeach
Feb 15 2020
prev sibling parent Boris Carvajal <boris2.9 gmail.com> writes:
On Friday, 14 February 2020 at 22:24:14 UTC, Ben Jones wrote:
 Hi all,

 I'm getting unexpected results while trying to process symbols 
 from a module, some of which are enums.

 Depending on whether or not I comment out the first static 
 foreach loop below, fullyQualifiedName gives me different 
 results in the second loop.
Looking more in detail it seems there is another peculiarity happening here, it could be that a template instance get reused (issue 14501). Not sure if it's really a bug or by design. https://issues.dlang.org/show_bug.cgi?id=14501
Feb 15 2020