www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - UDA inheritance

reply Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
Hello folks,

Is there any way to define UDAs such that they automatically 
inherit other UDA definitions?

For example, suppose I define:

     enum BaseUDA { A, B }

Is there a way to define `AnotherUDA` such that if `hasUDA!(T, 
AnotherUDA)` then it is a given that `hasUDA!(T, BaseUDA)` will 
also be true?  (And similarly for the `A`, `B` specializations?)

The use-case here is to create a UDA that defines some general 
distinction of code properties, and to allow downstream code to 
define its own more specialized cases of that distinction.

Thanks in advance for any thoughts or advice!

Thanks and best wishes,

       -- Joe
Sep 10 2020
next sibling parent reply drug <drug2004 bk.ru> writes:
On 9/10/20 4:06 PM, Joseph Rushton Wakeling wrote:
 Hello folks,
 
 Is there any way to define UDAs such that they automatically inherit 
 other UDA definitions?
 
 For example, suppose I define:
 
      enum BaseUDA { A, B }
 
 Is there a way to define `AnotherUDA` such that if `hasUDA!(T, 
 AnotherUDA)` then it is a given that `hasUDA!(T, BaseUDA)` will also be 
 true?  (And similarly for the `A`, `B` specializations?)
 
 The use-case here is to create a UDA that defines some general 
 distinction of code properties, and to allow downstream code to define 
 its own more specialized cases of that distinction.
 
 Thanks in advance for any thoughts or advice!
 
 Thanks and best wishes,
 
        -- Joe
Just a thought - couldn't you use classes for this? Get an UDA and check if it is a descendant of the specific class.
Sep 10 2020
parent Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
On Thursday, 10 September 2020 at 13:14:47 UTC, drug wrote:
 Just a thought - couldn't you use classes for this? Get an UDA 
 and check if it is a descendant of the specific class.
Yes, I did wonder about that, but it doesn't allow all the inference that I'm looking for. For example: class First { enum A; enum B; } class Second : First { } (Second.A) struct MySecond { } import std.traits : hasUDA; static assert(hasUDA!(MySecond, Second.A)); // OK! static assert(hasUDA!(MySecond, First.A)); // OK! static assert(hasUDA!(MySecond, Second)); // fails static assert(hasUDA!(MySecond, First)); // fails It's obvious _why_, of course, given how I set the above up. And this contrasts with what one can do with enums: enum Something { A, B } (Something.A) struct MySomething { } import std.traits : hasUDA; static assert(hasUDA!(MySomething, Something.A)); // OK! static assert(hasUDA!(MySomething, Something)); // OK! ... where I can check for the enum specialization _or_ the general enum type and have things just work. I'm looking, ideally, to be able to do both. I did try something like: enum First { A, B } enum Second : First { A = First.A, B = First.B } ... but that doesn't work. Hence why I thought it might be worth making a forum post.
Sep 10 2020
prev sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 10 September 2020 at 13:06:41 UTC, Joseph Rushton 
Wakeling wrote:
 `hasUDA!(T, AnotherUDA)`
...do you have to use hasUDA? The language works with class UDAs, but hasUDA doesn't support it. If you write your own test function though you can: ``import std.traits; class BaseUDA { static BaseUDA opCall(string a) { return new BaseUDA(a); } string a_; string a() { return a_; } this(string a) { this.a_ = a; } } class DerivedUDA : BaseUDA { static DerivedUDA opCall(string a) { return new DerivedUDA(a); } this(string a) { super(a); } } DerivedUDA("test") struct Test {} BaseUDA hasOurUda(T)() { BaseUDA found = null; foreach(uda; __traits(getAttributes, T)) { static if(is(typeof(uda) : BaseUDA)) found = uda; } return found; } pragma(msg, hasOurUda!Test); ``` Or, of course, if you are making your own function, you can still do a plain type == a || b, but the class is fairly natural for more user extension. the opCall there is not necessary, it just saves the `new`. This is equally valid: (new DerivedUDA("test")) struct Test {} You could also use static immutable class instances kinda like enum instances: --- import std.traits; class BaseUDA { string a_; string a() const { return a_; } this(string a) immutable { this.a_ = a; } static immutable A = new immutable BaseUDA("A"); static immutable B = new immutable BaseUDA("B"); } class DerivedUDA : BaseUDA { this(string a) immutable { super(a); } static immutable C = new immutable DerivedUDA("C"); } // or use B or C or wahtever (DerivedUDA.A) struct Test {} pragma(msg, __traits(getAttributes, Test)); immutable(BaseUDA) hasOurUda(T)() { // awkward to work around unreachable code warning BaseUDA found = null; foreach(uda; __traits(getAttributes, T)) { static if(is(typeof(uda) : const BaseUDA)) found = cast() uda; } return cast(immutable) found; } pragma(msg, hasOurUda!Test); --- so if you are willing to write your own test function there's a lot of options to consider. But if you're stuck with Phobos... well, back to the drawing bard.
Sep 10 2020