Thread overview
How can I get this UDA at compile time?
Feb 21, 2021
Jack
Feb 21, 2021
Jacob Carlborg
Feb 21, 2021
Jack
February 21, 2021
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?
February 21, 2021
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
February 21, 2021
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.