On Monday, 16 September 2013 at 06:47:40 UTC, ilya-stromberg wrote:
On Sunday, 15 September 2013 at 18:31:40 UTC, simendsjo wrote:
On Sunday, 15 September 2013 at 17:34:06 UTC, matovitch wrote:
Hi everyone,

I read the documentation about user defined attributes, but I don't see their uses. Ok, it'a a template expression you can link to a declaration, but what are they useful for ? (not sure about the syntax ;-))

Can you declare a template constraint as a user defined attribute to do something like :

void template_function_which_go_back_and_forth(@("Bidirectional") @("Range") BR)(BR br) {...}

This would be awesome (even if not really occidental) to do something like:

@("SmallTypeSet") @("MediumTypeSet") @("LargeTypeSet") Type

This could allow to build tree based category structure.

It enables declarative programming.
And because this is D, there is no runtime overhead.
A common use is to add semantics to types and instances that is difficult or very intrusive to do by creating structs/classes by hand.

A little validation example:

@nonNull // An instance shouldn't be allowed to be null
class C {
 @matches("[0-9]+")
 string someNumber;

 @interval!"(]"(0, 10) // (0, 10] range
 int someInt;
}

C c;
validate(c); // returns ["C is null", "someNumber doesn't match '[0-9]+'", "someInt is outside the interval '(0, 10]'"]

And ORMs usually use annotations:

@table("some_tablename")
class C {
 @id("id_field_name")
 int id;
}

Take a look at C# and Java libraries to see how many uses attributes/annotations - they are still quite new in D, so they are still underutilized. A very big difference is of course that UDAs are available at compile time :)

Can you print a full examle? For example, can you implement "matches" UDA and validate function. It's intresting how can I create new UDA and check if it's available for class/field.

I don't have a full example without adding a lot of code, but this partial
example might give you the gist of it.


// This is the type that validates
struct matches(string mustMatch)
{
    alias re = ctRegex!(mustMatch);

    static string[] validate(T)(const ref T t)
    {
        static if(!isSomeString!T)
static assert(0, "matches only works on strings, not "~T.stringof);
        return match(t, re).empty ? ["no match"] : null;
    }
}

// and this is the code that runs all validators for a variable
void validate(alias T)(ref Appender!(string[]) app)
{
    static if(isTupleWrapper!T)
    {
        validate!(T.Head)(app);
        validate!(T.Tail)(app);
    }
    else
    {
        foreach(memberAttr; getValidaterAttrs!T)
        {
            foreach(attr; memberAttr.Tail)
            {
                foreach(msg; attr.validate(T))
                    if(msg.length)
                        app.put(msg);
            }
        }
    }
}

// .. And here is some of the plumbing

string[] validate(Vars...)()
{
    auto app = appender!(string[])();
    validate!Vars(app);
    return app.data();
}


// The getMembersAndAttributesWhere are templates in my little library that isn't released. Uses quite some custom __traits stuff, but it's basically __traits(getAttributes
template getValidaterAttrs(alias T)
{
alias getValidaterAttrs = TypeTuple!(getMembersAndAttributesWhere!(T, isValidationAttr).Elements, getMembersAndAttributesWhere!(TypeOf!T, isValidationAttr).Elements);
}

// Well.. Incomplete
template isValidationAttr(alias T)
{
    enum isValidationAttr = hasMember!(TypeOf!T, "validate");
}

Reply via email to