www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How can I get this UDA at compile time?

reply Jack <jckj33 gmail.com> writes:
I've had a struct like this:

struct Attr
{
	string value;
}

struct Foo
{
	 (Attr("a attr"))
	enum a = Foo(10);

	 (Attr("b attr"))
	enum b = Foo(11);


	int x;
	int y;
	bool doY = true;
	
	int value()
	{

		return x;
	}

}

I'd like to get that atrribute's value at compile time, something 
like this:

	enum s = Foo.a.baa;
	enum s2 = Foo.b.baa;
	writeln(s); // a attr
	writeln(s2); // b attr

I did this:

	string baa()
	{
		import std.traits : getUDAs, hasUDA;

		static foreach(field; __traits(allMembers, Foo))
		{{
			alias m = __traits(getMember, Foo, field);
			static if(is(typeof(m) == Foo))
			{
				if(m.value == this.value)
					return getUDAs!(m, Attr)[0].value;
			}
		}}

		return null;
	}

that was working fine, but I needed to switch value property from 
Foo struct, so that I can't read this value at CTFE anymore, so 
this fails now:

			if(m.value == this.value)
					return getUDAs!(m, Attr)[0].value;

How can I solve this?
Feb 20 2021
parent reply Jacob Carlborg <doob me.com> writes:
On 2021-02-21 07:12, Jack wrote:
 I've had a struct like this:
 
 struct Attr
 {
      string value;
 }
 
 struct Foo
 {
       (Attr("a attr"))
      enum a = Foo(10);
 
       (Attr("b attr"))
      enum b = Foo(11);
 
 
      int x;
      int y;
      bool doY = true;
 
      int value()
      {
 
          return x;
      }
 
 }
 
 I'd like to get that atrribute's value at compile time, something like 
 this:
 
      enum s = Foo.a.baa;
      enum s2 = Foo.b.baa;
      writeln(s); // a attr
      writeln(s2); // b attr
 
 I did this:
 
      string baa()
      {
          import std.traits : getUDAs, hasUDA;
 
          static foreach(field; __traits(allMembers, Foo))
          {{
              alias m = __traits(getMember, Foo, field);
              static if(is(typeof(m) == Foo))
              {
                  if(m.value == this.value)
                      return getUDAs!(m, Attr)[0].value;
              }
          }}
 
          return null;
      }
 
 that was working fine, but I needed to switch value property from Foo 
 struct, so that I can't read this value at CTFE anymore, so this fails now:
 
              if(m.value == this.value)
                      return getUDAs!(m, Attr)[0].value;
 
 How can I solve this?
You can't look at the value when trying to find the correct member. You need to look at the name. I don't think it's possible to solve that with the exact same API as you have used above. The simplest solution would be to just use `__traits(getAttributes)` and wrap that in a help function: string getAttribute(T, string name)() { return __traits(getAttributes, mixin(T.stringof, ".", name))[0].value; } void main() { writeln(getAttribute!(Foo, "a")); } Or you can create a proxy struct and use opDispatch like this, to get something a bit closer to your original example: // add this to the Foo struct static Proxy attributes() { return Proxy(); } struct Proxy { string opDispatch(string name)() { return __traits(getAttributes, mixin("Foo.", name))[0].value; } } void main() { writeln(Foo.attributes.a); } -- /Jacob Carlborg
Feb 21 2021
parent Jack <jckj33 gmail.com> writes:
On Sunday, 21 February 2021 at 09:30:14 UTC, Jacob Carlborg wrote:
 On 2021-02-21 07:12, Jack wrote:
 I've had a struct like this:
 
 struct Attr
 {
      string value;
 }
 
 struct Foo
 {
       (Attr("a attr"))
      enum a = Foo(10);
 
       (Attr("b attr"))
      enum b = Foo(11);
 
 
      int x;
      int y;
      bool doY = true;
 
      int value()
      {
 
          return x;
      }
 
 }
 
 I'd like to get that atrribute's value at compile time, 
 something like this:
 
      enum s = Foo.a.baa;
      enum s2 = Foo.b.baa;
      writeln(s); // a attr
      writeln(s2); // b attr
 
 I did this:
 
      string baa()
      {
          import std.traits : getUDAs, hasUDA;
 
          static foreach(field; __traits(allMembers, Foo))
          {{
              alias m = __traits(getMember, Foo, field);
              static if(is(typeof(m) == Foo))
              {
                  if(m.value == this.value)
                      return getUDAs!(m, Attr)[0].value;
              }
          }}
 
          return null;
      }
 
 that was working fine, but I needed to switch value property 
 from Foo struct, so that I can't read this value at CTFE 
 anymore, so this fails now:
 
              if(m.value == this.value)
                      return getUDAs!(m, Attr)[0].value;
 
 How can I solve this?
You can't look at the value when trying to find the correct member.
It doesn't work when the value is unique and know at compile-time, as it was previously. So this worked:
          static foreach(field; __traits(allMembers, Foo))
          {{
              alias m = __traits(getMember, Foo, field);
              static if(is(typeof(m) == Foo))
              {
                  if(m.value == this.value)
                      return getUDAs!(m, Attr)[0].value;
              }
          }}
This could retrieve the attribute at compile time by value but i did changes in the struct and the member value wasn't know at compile time anymore.
 You need to look at the name.
That's I'm looking for. Is there a way to get the idenfifier of the current instance, from within the class? for example: struct Foo { enum x = Foo(10); enum y = Foo(11); string myID() { eum s = some magic with traits? return s; } } writeln(Foo.x.myID); // x writeln(Foo.y.myID); // y that would solve my problem, I would just pass that idenfifier to __traits(getMember, Foo, x) then get what I want with getUDAs() I don't think it's
 possible to solve that with the exact same API as you have used 
 above. The simplest solution would be to just use 
 `__traits(getAttributes)` and wrap that in a help function:

 string getAttribute(T, string name)()
 {
      return __traits(getAttributes, mixin(T.stringof, ".", 
 name))[0].value;
 }

 void main()
 {
     writeln(getAttribute!(Foo, "a"));
 }
the main isssue is get "a" identifier, as I mentioned previously.
 Or you can create a proxy struct and use opDispatch like this, 
 to get something a bit closer to your original example:

 // add this to the Foo struct
 static Proxy attributes()
 {
     return Proxy();
 }

 struct Proxy
 {
     string opDispatch(string name)()
     {
         return __traits(getAttributes, mixin("Foo.", 
 name))[0].value;
     }
 }

 void main()
 {
     writeln(Foo.attributes.a);
 }
your proxy struct and opDispatch() give me a good idea how do that, something like this: void main() { writeln(P.Foo); } struct S { string name; } struct P { static auto ref opDispatch(string member)() { writeln("member = ", member); // save this somewhere alias m = __traits(getMember, A, member); return m; } } struct A { (S("attr foo")) enum Foo = A(10); (S("attr baa")) enum Baa = A(11); int v; } now I got the member string but I still need to figure out where to salve it to use from within the A struct. add a string id to struct A wouldn't work for Foo and Baa because they are enum. I have to save it somewhere else. I static array doesn't work either because it isn't run at CTFE.
Feb 21 2021