Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions attribute.dd
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,117 @@ $(P
they can still provide $(SINGLEQUOTE base class functionality.)
)

$(SECTION2 $(LNAME2 uda, User Defined Attributes),

$(P
User Defined Attributes (UDA) are compile time expressions that can be attached
to a declaration. These attributes can then be queried, extracted, and manipulated
at compile time. There is no runtime component to them.
)

$(P
Grammatically, a UDA is a StorageClass:
)

$(GRAMMAR
$(GNAME StorageClass):
$(GLINK UserDefinedAttribute)

$(GNAME UserDefinedAttribute):
@(ArgumentList)
@CallExpression
)
$(P
And looks like:
)

---
@(3) int a;
@("string", 7) int b;

enum Foo;
@Foo int c;

struct Bar
{
int x;
}

@Bar(3) int d;
---

$(P
If there are multiple UDAs in scope for a declaration, they are concatenated:
)

---
@(1) {
@(2) int a; // has UDA's (1, 2)
@("string") int b; // has UDA's (1, "string")
}
---

$(P
UDA's can be extracted into an expression tuple using __traits:
)

---
@('c') string s;
pragma(msg, __traits(getAttributes, s)); // prints tuple('c')
---
)

$(P
If there are no user defined attributes for the symbol, an empty tuple is returned.
The expression tuple can be turned into a manipulatable tuple:
)

---
template Tuple (T...)
{
alias T Tuple;
}

enum EEE = 7;
@("hello") struct SSS { }
@(3) { @(4) @EEE @SSS int foo; }

alias Tuple!(__traits(getAttributes, foo)) TP;

pragma(msg, TP); // prints tuple(3, 4, 7, (SSS))
pragma(msg, TP[2]); // prints 7
---

$(P
Of course the tuple types can be used to declare things:
)

---
TP[3] a; // a is declared as an SSS
---

$(P
The attribute of the type name is not the same as the attribute of the variable:
)

---
pragma(msg, __traits(getAttributes, typeof(a))); // prints tuple("hello")
---

$(P
Of course, the real value of UDA's is to be able to create user defined types with
specific values. Having attribute values of basic types does not scale.
The attribute tuples can be manipulated like any other tuple, and can be passed as
the argument list to a template.
)

$(P
Whether the attributes are values or types is up to the user, and whether later
attributes accumulate or override earlier ones is also up to how the user
interprets them.
)
)

)

Macros:
Expand Down
33 changes: 33 additions & 0 deletions traits.dd
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ $(GNAME TraitsKeyword):
$(GBLINK isLazy)
$(GBLINK hasMember)
$(GBLINK identifier)
$(GBLINK getAttributes)
$(GBLINK getMember)
$(GBLINK getOverloads)
$(GBLINK getProtection)
Expand Down Expand Up @@ -306,6 +307,38 @@ $(H2 $(GNAME identifier))
for that symbol as a string literal.
)

$(SECTION2 $(GNAME getAttributes),
$(P
Takes one argument, a symbol. Returns a tuple of all attached user defined attributes.
If no UDA's exist it will return an empty tuple.
)

$(P
For more information, see: $(DDSUBLINK attribute, uda, User Defined Attributes)
)

---
@(3) int a;
@("string", 7) int b;

enum Foo;
@Foo int c;

pragma(msg, __traits(getAttributes, a));
pragma(msg, __traits(getAttributes, b));
pragma(msg, __traits(getAttributes, c));
---

$(P
Prints:
)

$(CONSOLE
tuple(3)
tuple("string", 7)
tuple((Foo))
)
)

$(H2 $(GNAME getMember))

Expand Down