www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Generating code based on UDA

reply "Rares Pop" <fusionbean github.com> writes:
Hello everyone,

I am trying to understand UDA traits scoping while mixing in code.
Aiming to generate code based on UDA I wonder if the following is 
possible:

class A
{
     Inject
    Logger logger;

     Inject
    SomeOtherClass dependency;

    mixin injections!(A)

...
}

In "injections" function I want to iterate through members 
annotated with the  Inject attribute and generate some specific 
code.
So far in my attempts the compiler complains about unknown 
identifier A.
Should the A class be available to the compiler at the time of 
the mixin invocation?

Thanks for your help,
Rares
Oct 25 2014
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 25 October 2014 at 13:37:56 UTC, Rares Pop wrote:
 Aiming to generate code based on UDA I wonder if the following 
 is possible:
Yes, and copy/pasting that works for me...
Oct 25 2014
parent reply "Rares Pop" <fusionbean github.com> writes:
Thanks for the quick response.
What do you mean by copy/pasting ?

On Saturday, 25 October 2014 at 13:40:56 UTC, Adam D. Ruppe wrote:
 On Saturday, 25 October 2014 at 13:37:56 UTC, Rares Pop wrote:
 Aiming to generate code based on UDA I wonder if the following 
 is possible:
Yes, and copy/pasting that works for me...
Oct 25 2014
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 25 October 2014 at 13:45:29 UTC, Rares Pop wrote:
 What do you mean by copy/pasting ?
I literally copied the code in your post (and fixed a missing semicolon) and got it to compile. Passing A as an argument to injections should work. You can also use this and typeof(this) inside the injections template code to access the class. It should all work.
Oct 25 2014
parent reply "Rares Pop" <fusionbean github.com> writes:
On Saturday, 25 October 2014 at 13:53:35 UTC, Adam D. Ruppe wrote:
 On Saturday, 25 October 2014 at 13:45:29 UTC, Rares Pop wrote:
 What do you mean by copy/pasting ?
I literally copied the code in your post (and fixed a missing semicolon) and got it to compile. Passing A as an argument to injections should work. You can also use this and typeof(this) inside the injections template code to access the class. It should all work.
Taking this one step further, it looks like the attributes are not available at the mixin scope. Here is my code: ------ struct Inject { // immutable Scope scoped; } static string injections(T)() { pragma(msg, "injections for : ", T); string result; foreach(member; __traits(allMembers,T)) { enum fullName = format("%s.%s", T.stringof, member); pragma(msg, "member: ", fullName); auto attributes = __traits(getAttributes, fullName); enum dbg_msg = format ("%s attributes are %s", fullName, attributes.stringof); pragma(msg, dbg_msg); foreach(attr;attributes){ pragma(msg, "Checking attribute", attr); } } return result; } class A { this(){ } } class B { Inject A a; mixin(injections!(B)); } --- when compiling this code this is the output I get: ---- Compiling using dmd... injections for : B member: B.a B.a attributes are tuple() member: B.toString B.toString attributes are tuple() member: B.toHash B.toHash attributes are tuple() member: B.opCmp B.opCmp attributes are tuple() member: B.opEquals B.opEquals attributes are tuple() member: B.Monitor B.Monitor attributes are tuple() member: B.factory B.factory attributes are tuple() ------ B.a attributes are an empty tuple even though the member is annotated with Inject. Any ideas why?
Oct 25 2014
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 10/25/2014 07:45 AM, Rares Pop wrote:
 On Saturday, 25 October 2014 at 13:53:35 UTC, Adam D. Ruppe wrote:
 On Saturday, 25 October 2014 at 13:45:29 UTC, Rares Pop wrote:
 What do you mean by copy/pasting ?
I literally copied the code in your post (and fixed a missing semicolon) and got it to compile. Passing A as an argument to injections should work. You can also use this and typeof(this) inside the injections template code to access the class. It should all work.
Taking this one step further, it looks like the attributes are not available at the mixin scope. Here is my code: ------ struct Inject { // immutable Scope scoped; } static string injections(T)() { pragma(msg, "injections for : ", T); string result; foreach(member; __traits(allMembers,T)) {
import std.string;
          enum fullName = format("%s.%s", T.stringof, member);
          pragma(msg, "member: ", fullName);
          auto attributes = __traits(getAttributes, fullName);
You must mixin fullName: auto attributes = __traits(getAttributes, mixin(fullName));
          enum dbg_msg = format ("%s attributes are %s", fullName,
 attributes.stringof);
          pragma(msg, dbg_msg);
          foreach(attr;attributes){
Replace the body of this foreach with the following: pragma(msg, "Checking attribute of type", typeof(attr)); static if (is (typeof(attr) == Inject)) { pragma(msg, "Found one"); // Let's inject something: result ~= q{ int foo() { return 42; } }; }
              pragma(msg, "Checking attribute", attr);
          }

      }
      return result;
 }

 class A {

      this(){
      }
 }

 class B {

       Inject A a;
For an unknown reason to me, that UDA wants an Inject object, not the type itself: Inject() A a; I am puzzled with that...
      mixin(injections!(B));

 }
Then it works with the following main: void main() { auto b = new B(); assert(b.foo() == 42); // It worked! :) } Here is the complete program: struct Inject { // immutable Scope scoped; } static string injections(T)() { pragma(msg, "injections for : ", T); string result; foreach(member; __traits(allMembers,T)) { import std.string; enum fullName = format("%s.%s", T.stringof, member); pragma(msg, "member: ", fullName); auto attributes = __traits(getAttributes, mixin(fullName)); enum dbg_msg = format ("%s attributes are %s", fullName, attributes.stringof); pragma(msg, dbg_msg); foreach(attr;attributes){ pragma(msg, "Checking attribute of type", typeof(attr)); static if (is (typeof(attr) == Inject)) { pragma(msg, "Found one"); // Let's inject something: result ~= q{ int foo() { return 42; } }; } } } return result; } class A { this(){ } } class B { Inject() A a; mixin(injections!(B)); } void main() { auto b = new B(); assert(b.foo() == 42); // It worked! :) } Ali
Oct 25 2014
next sibling parent "Rares Pop" <fusionbean github.com> writes:
Ali,
Many thanks for your help.

What is the rationale for mixin(fullName) ?
Oct 25 2014
prev sibling parent reply "Rares Pop" <fusionbean github.com> writes:
Ali,
Many thanks for your help.
Indeed it worked. What is the rationale behind the 
mixin(fullName) ?

However, in my project the injections function, the  Inject UDA 
struct and some other dependencies are defined in a library 
(libinfuse).

In this format the compiler gives the undefined identifier error 
I was mentioning in my first post.
"source/infuse/injector.d-mixin-148(148): Error: undefined 
identifier B"

 From what I read in the documentation, source mixins should have 
the instantiation scope.
Is this a dmd compiler bug?

Thanks again for your input guys,
Rares
Oct 25 2014
next sibling parent reply "Rares Pop" <fusionbean github.com> writes:
I've uploaded the code here:
https://github.com/fusionbeam/infuse
Oct 25 2014
parent "Rares Pop" <fusionbean github.com> writes:
On Saturday, 25 October 2014 at 16:01:29 UTC, Rares Pop wrote:
 I've uploaded the code here:
 https://github.com/fusionbeam/infuse
compiling with ldc2 exhibits the same behaviour. 'Error: undefined identifier B'
Oct 25 2014
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 10/25/2014 08:56 AM, Rares Pop wrote:

 Indeed it worked. What is the rationale behind the mixin(fullName) ?
__traits(getAttributes) requires a symbol but fullName is a string. Mixing it in as code fulfills the requirement.
 However, in my project the injections function, the  Inject UDA struct
 and some other dependencies are defined in a library (libinfuse).
I am afraid it needs to be changed. :-/
 In this format the compiler gives the undefined identifier error I was
 mentioning in my first post.
 "source/infuse/injector.d-mixin-148(148): Error: undefined identifier B"
I found two solutions: a) Do not define 'attributes' at all and use __traits(getAttributes) directly in the foreach loop: foreach (attr; __traits(getAttributes, mixin(fullName))) { b) Define attributes as a typeof of __traits(getAttributes) and use that in the foreach loop: alias attributes = typeof(__traits(getAttributes, mixin(fullName))); foreach (attr; attributes) { I think what happens in both cases is that the entity that we iterate over maintains its TypeTuple'ness without trying to produce a value out of its members.
 Is this a dmd compiler bug?
I don't know but it is very confusing. Ali
Oct 25 2014
parent reply "Rares Pop" <fusionbean github.com> writes:
I think it is a bug.
Executing linked code from a mixin statement should not reduce 
the scope of the mixin, IMHO.

I will file a bug report.

On Saturday, 25 October 2014 at 21:35:44 UTC, Ali Çehreli wrote:
 On 10/25/2014 08:56 AM, Rares Pop wrote:

 Indeed it worked. What is the rationale behind the
mixin(fullName) ? __traits(getAttributes) requires a symbol but fullName is a string. Mixing it in as code fulfills the requirement.
 However, in my project the injections function, the  Inject
UDA struct
 and some other dependencies are defined in a library
(libinfuse). I am afraid it needs to be changed. :-/
 In this format the compiler gives the undefined identifier
error I was
 mentioning in my first post.
 "source/infuse/injector.d-mixin-148(148): Error: undefined
identifier B" I found two solutions: a) Do not define 'attributes' at all and use __traits(getAttributes) directly in the foreach loop: foreach (attr; __traits(getAttributes, mixin(fullName))) { b) Define attributes as a typeof of __traits(getAttributes) and use that in the foreach loop: alias attributes = typeof(__traits(getAttributes, mixin(fullName))); foreach (attr; attributes) { I think what happens in both cases is that the entity that we iterate over maintains its TypeTuple'ness without trying to produce a value out of its members.
 Is this a dmd compiler bug?
I don't know but it is very confusing. Ali
Oct 25 2014
parent "Rares Pop" <fusionbean github.com> writes:
I have found the problem.
It was the nested mixin that was causing the scope degradation 
(not sure if that is intended behaviour).

The correct way to iterate through members and get their 
attribute is  like this:

foreach(member; __traits(allMembers,T))
	{		
		enum fullName = format("%s.%s", T.stringof, member);
		pragma(msg, "member: ", fullName);
		foreach(attr; __traits(getAttributes, __traits(getMember, T, 
member)))
		{
....
Oct 26 2014
prev sibling parent Shammah Chancellor <anonymous coward.com> writes:
On 2014-10-25 13:37:54 +0000, Rares Pop said:

 Hello everyone,
 
 I am trying to understand UDA traits scoping while mixing in code.
 Aiming to generate code based on UDA I wonder if the following is possible:
 
 class A
 {
      Inject
     Logger logger;
 
      Inject
     SomeOtherClass dependency;
 
     mixin injections!(A)
 
 ...
 }
 
 In "injections" function I want to iterate through members annotated 
 with the  Inject attribute and generate some specific code.
 So far in my attempts the compiler complains about unknown identifier A.
 Should the A class be available to the compiler at the time of the 
 mixin invocation?
 
 Thanks for your help,
 Rares
Very much possible. Since I don't see what your template is doing, I'm going to give it a guess: class A{ mixin injections; } template injections { .... typeof(this); // e.g. foreach( tra; __traits(getAttributes, typeof(this))) } typeof(this) will be A.
Oct 25 2014