www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Question about UDAs

reply Cecil Ward <cecil cecilward.com> writes:
When practically speaking would you use UDAs? A real-world 
use-case? I’ve seen them in use already for core language 
features instead of keywords like "pure", and I suppose this 
choice keeps the number of keywords down and the result is 
perhaps easier to extend. The motivation for these usages is 
clear but I don’t understand how I might use them in my own code. 
Ali Çehreli’s book mentions them briefly with an example but that 
doesn’t seem to qualify as a realistic use-case.
Aug 02 2020
next sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Monday, 3 August 2020 at 03:00:08 UTC, Cecil Ward wrote:
 When practically speaking would you use UDAs? A real-world 
 use-case?
They are useful when you want to attach some kind of metadata to the declarations for a library to read. For example, my script.d looks for ` scriptable` for methods that it should expose to the script user. My cgi.d uses things like ` URLName("foo")` and/or ` DisplayName("whatever")` if you want to override the auto-wrapper's default strings. My day job work project uses it for user-visible documentation of their functions too, accessible from a command line interface.
 I’ve seen them in use already for core language features 
 instead of keywords like "pure"
i wouldn't call those UDAs because they aren't user-defined... but yeah it is the same syntax there basically too.
Aug 02 2020
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Aug 03, 2020 at 03:00:08AM +0000, Cecil Ward via Digitalmars-d-learn
wrote:
 When practically speaking would you use UDAs? A real-world use-case?
There are probably more use cases than this, but for me, their primary usefulness is in declarative programming and compile-time introspection. Here's one specific use case: serialization. I have a bunch of structs and classes that I want to serialize, and instead of writing tons and tons of boilerplate, I use __traits(allMembers) to introspect a generic type T and generate serialization code for it. But sometimes some fields should not be serialized (e.g., they are transients like caches and stuff). Or sometimes certain fields may need special treatment, like a different serialization method depending on domain-specific information about their contents. I *could* hard-code this knowledge into the serialization code, but UDAs provide a better alternative: I tag my types with various UDAs recognized by the serialization system, so that when it encounters, say, a string tagged hexDigits, it knows that it can use a more compact representation by parsing the string into binary and storing it as a compact blob, for example. Or if a field should not be serialized, I'd tag it dontSerialize and the serialization code skips over it. By using UDAs instead of hard-coding into the serialization code, the serialization can be made generic and reusable across projects. Other use cases include automatically creating database schemas based on types: like a bunch of structs representing records, and UDAs to tag which fields should be indexed, which fields have constraints, etc.. Then the database backend code can just introspect these types and automatically generate schemas, query code, etc.. Since UDAs can be arbitrary types, it actually has a lot of uses. For example, you can use them to inject code for processing data, e.g., by using a struct as UDA with a method that takes the data and performs some operation on it. The generic code that reads the UDA can then pass the data to the method in a completely agnostic way that lets you separate concerns very cleanly. T -- In a world without fences, who needs Windows and Gates? -- Christian Surchi
Aug 03 2020
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 8/2/20 8:00 PM, Cecil Ward wrote:

 Ali =C3=87ehreli=E2=80=99s book mentions them briefly with an example
 but that doesn=E2=80=99t seem to qualify as a realistic use-case.
The XML example I chose there qualifies as serialization like H. S. Teoh = mentions. UDAs on user-defined type members are for marking them for=20 later introspection in use cases like "do this for all members but take=20 UDAs into account." For example, the UDA in my example contributes as=20 "serialize all members as XML but obfuscate the members that have a=20 special UDA." UDAs were added to D by a request from Manu Evans and that's when I=20 learned them. In one of Manu's use cases they would put a Tweakable=20 attribute to certain struct members. The effect of that attribute would=20 be to compile special code that would expose that member in a dialog box = where the developer would "tweak" its value to see how the program (a=20 game) would behave at specific values of that member. The awesomeness comes from the fact that once they have this Tweakable=20 machinery, they don't change their code at all: They put that attribute=20 to certain members during development, find good values and then remove=20 it; perhaps in half an hour. The only addition to code is one Tweakable = attribute and some magic produces a dialog box; then they remove the=20 attribute. Pretty cool. :) Manu's presentatian is available here: https://www.youtube.com/watch?v=3DFKceA691Wcg Slide 25 at minute 18:30 is one spot he talks about it. Ali
Aug 03 2020
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Aug 03, 2020 at 08:16:57PM -0700, Ali Çehreli via Digitalmars-d-learn
wrote:
[...]
 UDAs were added to D by a request from Manu Evans and that's when I
 learned them. In one of Manu's use cases they would put a  Tweakable
 attribute to certain struct members. The effect of that attribute
 would be to compile special code that would expose that member in a
 dialog box where the developer would "tweak" its value to see how the
 program (a game) would behave at specific values of that member.
 
 The awesomeness comes from the fact that once they have this
  Tweakable machinery, they don't change their code at all: They put
 that attribute to certain members during development, find good values
 and then remove it; perhaps in half an hour. The only addition to code
 is one  Tweakable attribute and some magic produces a dialog box; then
 they remove the attribute. Pretty cool. :)
I've also used it for generating getopt-like code for parsing command-line parameters. You might have several subsystems in your program, each of which comes with a set of parameters that can be configured; instead of sprinkling this information across multiple places (once in the subsystem to read the values, once in the call to getopt to parse the option, once in the code for display detailed description of the setting, once in configuration file parsing code to basically do the same thing as getopt except with a config file, ad nauseaum), just create a struct that contains the parameters for each subsystem, then use UDAs to decorate each setting with command-line option name, help text, value ranges, or even custom parsing functions if you want to get fancy. Then write a generic option-parsing function that introspects the UDAs to generate help text, option names, default values, precedences, etc.. Another generic function for parsing the config file. Then the next time you want to add a setting, it's just a matter of adding another field to your struct, tag it with the appropriate UDAs, and it will "magically" appear in your command-line, config file parsing, and in-program help text without further ado. Best of all, once you build this infrastructure, you can easily reuse it across different programs: the getopt wrapper, help text generator, config file parser are all completely driven by introspection and UDAs, so there's nothing program-specific about them. Just copy-n-paste them into another project, and create your structs, and you're all set. :-) T -- "I'm not childish; I'm just in touch with the child within!" - RL
Aug 03 2020