www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Traits and UDAs

reply "Chris Williams" <yoreanon-chrisw yahoo.co.jp> writes:
I decided to write up a little application to test using User 
Defined Attributes and have one question. Here is a working 
application that allows me to mark certain members of a function 
to be dumped:

import std.stdio;

enum Dump = "Dump";
template Dumper(T) {
	void dump() {
		foreach (mem; __traits(allMembers, T)) {
			auto attributes = __traits(getAttributes, __traits(getMember, 
T, mem));
			static if ( attributes.length > 0 && __traits(getAttributes, 
__traits(getMember, T, mem))[0] == Dump ) {
				writeln(mem);
			}
		}
	}
}

class Foo {
	 Dump int hello;
	
	 Dump int world;
	
	int bye;
	
	mixin Dumper!(Foo);
}

int main(string[] args) {
	Foo f = new Foo();
	f.dump();
	
	return 0;
}


The eighth line bothers me, though. It seems like I should be 
able to write:

if (attributes.length > 0 && attributes[0] == Dump)

However, that fails to compile with the message:

test.d(7): Error: variable _attributes_field_0 cannot be read at 
compile time
test.d(8): Error: expression !!(_attributes_field_0 == "Dump") is 
not constant or does not evaluate to a bool
test.d(7): Error: variable _attributes_field_0 cannot be read at 
compile time
test.d(8): Error: expression !!(_attributes_field_0 == "Dump") is 
not constant or does not evaluate to a bool

Any thoughts?
Jun 06 2013
next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
Here's how I do it:

enum Dump; // just a type, no value here

template Dumper(T) {
    void dump() {
      foreach (mem; __traits(allMembers, T)) {
         // loop over the attrs instead of try to store them in a 
var
         foreach(attr; __traits(getAttributes, __traits(getMember, 
T, mem)))
            // identify them by type rather than value
            static if ( is(attr == Dump)) {
                   writeln(mem);
            }
         }
     }
}




Or some helper functions might be good. Here's the ones I did:

template hasAnnotation(alias f, Attr) {
	bool helper() {
		foreach(attr; __traits(getAttributes, f))
			static if(is(attr == Attr) || is(typeof(attr) == Attr))
				return true;
		return false;

	}
	enum bool hasAnnotation = helper;
}

template hasValueAnnotation(alias f, Attr) {
	bool helper() {
		foreach(attr; __traits(getAttributes, f))
			static if(is(typeof(attr) == Attr))
				return true;
		return false;

	}
	enum bool hasValueAnnotation = helper;
}



template getAnnotation(alias f, Attr) if(hasValueAnnotation!(f, 
Attr)) {
	auto helper() {
		foreach(attr; __traits(getAttributes, f))
			static if(is(typeof(attr) == Attr))
				return attr;
		assert(0);
	}

	enum getAnnotation = helper;
}





hasAnnotation!(something, Dump) tells you if it is just there

hasValueAnnotation tells you if the annoation has a value (enum 
Dump; won't, but struct Dump { int option; } would).

getAnnotation fetches the value.



The "something" argument there is a symbol, e.g. 
__traits(getMember, T, mem). The second argument is the type 
you're interested in.
Jun 06 2013
prev sibling parent "Dicebot" <m.strashun gmail.com> writes:
On Thursday, 6 June 2013 at 17:50:08 UTC, Chris Williams wrote:
 ...
It will work if use "enum" instead of "auto" for storing attribute tuple. If compile-time value is needed you almost always want to store it in enum.
Jun 06 2013