D Programming: How to Define and Use Custom Attributes

cc cc at nevernet.com
Sun Sep 10 16:22:38 UTC 2023


On Wednesday, 6 September 2023 at 18:54:26 UTC, Soham Mukherjee 
wrote:
> I'm exploring the D programming language and I'm curious about 
> custom attributes. How can I define and use custom attributes 
> in D to add metadata or behavior to my code?

UDAs can be extremely useful for iterating through select members 
of a type when reading data from an input source, such as XML or 
JSON data.  A very basic setup might look something like this 
(very stripped example written from memory, lot of blanks to be 
filled in):

```d
struct XMLField {} // We'll use an empty struct for our UDA 
rather than enum since it's
                    // guaranteed to be a globally unique symbol

class Person {
	@XMLField { // Everything in this block gets the @XMLField UDA
		string name;
		int age;
	}
}

auto data = `
<People>
	<Person>
		<name>Bob</name>
		<age>34</age>
	</Person>
	<Person>
		<name>Joe</name>
		<age>20</age>
	</Person>
</People>
`;

auto xml = MyXMLParser(data); // supply your own xml parser
foreach (node; xml.find("Person")) {
	auto p = new Person;
	readFromXML(p, node);
}

void readFromXML(T)(T t, XMLNode node) {
	foreach (idx, field; T.tupleof) { // iterate through all member 
variables on class
		static if (hasUDA!(T.tupleof[idx], XMLField)) { // If it has 
the XMLField UDA...
			enum NAME = field.stringof; // the name of the member (name, 
age, etc)
			alias TYPE = typeof(field);
			if (auto text = node.find(NAME).text) { // find child XML node 
that matches member
				try {
					t.tupleof[idx] = text.to!TYPE; // std.conv.to to convert 
text to the appropriate type
				} catch (ConvException e) {
					...
				}
			}
		}
	}
}
```

An ultimate goal is often to use introspection to minimize the 
number of places, in code, that need to be "touched" every time a 
data structure is modified or new fields added.  In an older, 
more straightforward program, if I added a new field to `Person`, 
I would also need to remember to change the files responsible for 
reading that field from the input data, validating it, etc.  
Here, I only need to add the new field with the UDA to the class, 
and the rest is taken care of (the UDA, itself, is not strictly 
necessary here, but it helps to let the system know which fields 
to process and which to ignore!).  Of course, in a real-world 
program, significantly more care would need to be taken to 
validate the data, handle errors, conversions, missing entries, 
duplicate entries, and so on.  But once your system is solid, it 
minimizes the pain of upgrading the important stuff.

Some other richer ideas include:
* Attributing classes to specify how to dynamically bind to LUA
* Configuration settings that restrict allowable values to be 
stored from a text command console
* Marking functions to be available via an RPC library and how to 
mangle them




More information about the Digitalmars-d mailing list