Extracting user defined attributes on function parameters

Adam D. Ruppe destructionator at gmail.com
Wed Apr 15 00:01:51 UTC 2020


On Tuesday, 14 April 2020 at 21:54:14 UTC, Jean-Louis Leroy wrote:
> O.....kay. It looks like `is` is D's Swiss army chainsaw.

Aye, is and __traits are the two built-in compile-time reflection 
avenues. The phobos std.traits things (for the most part, look at 
the source for the default parameter values thing and lol you'll 
see some of them get quite involved) wrap them.

is() is a bit weird, but I described it in my "D Cookbook" to 
some success... and writing that description helped me make sense 
of it. The docs list like seven forms of it, but they are mostly 
just one big thing with a lot of optional pieces you can leave 
out, coupled with a little bit of magic when you use certain 
keywords, like __parameters.

https://dlang.org/spec/expression.html#is_expression

I like to think of it as a kind of magic variable declaration

is(SomeExistingType yourOptionalIdenfier == some_optional_pattern)

(or you can use : instead of == for a looser match. == is 
equality, and : indicates implicit conversion, like how class Foo 
: Interface can implicitly convert to Interface, so `is(Foo : 
Interface)` passes.


If you leave off both the ident and the pattern, it just check is 
the type is a type:

static if(is(Object)) // passed because Object is a type

static if(is(10)) // fails because 10 isn't a type, it is a value

static if(is(adasdsa)) // fails because undefined identifier


If you provide the optional identifier, it aliases the match 
inline for you. I think of the syntax as being like a variable 
declaration. So just like

int a; // defines a new variable with the name "a"

static if(is(Object a)) // defines a new alias with the name "a" 
being the same as Object



Then you add on the pattern. These are generally written like the 
variable declaration too, but you can define placeholders. Simple 
example:

static if(is(typeof(a) == int))

more complex example:

static if(is(typeof(a) == T[], T))


In that one, the pattern is "T[]", looking like a type, but then 
since I specified ", T" afterward, it means T is a placeholder.

So that would match if typeof(a) is some kind of array, and then 
T gets the element type.

int[] a;
static if(is(typeof(a) == T[], T)) // passes because a is an 
array, T == int


Those can get pretty complex because you're allowed to add as 
many placeholders as you need for an arbitrarily complex pattern, 
though I prefer to keep it kinda simple and use two levels of 
static if to cover more complicated things.

Then the last magic is the pattern on the right-hand side can be 
various keywords.

static if(is(Object == class))

passes if Object is a class. You can also do that with struct, 
interface, union, const, immutable, and shared.

But if you look at the docs, you see there are a few other magic 
keywords too, like `enum`, return, function, delegate, super, and 
__parameters. They still match what you expect basically - if it 
is a function pointer, or an enum, etc., but the real magic is 
they change what the alias you can define in the is() expression 
refers to. So then it isn't just a convenience alias for the 
condition, it actually becomes an aspect of that type, like the 
return type, or the params, or the parent class.

So yes a swiss army chainsaw :P but once you know its few tricks 
and patterns it isn't as hard as the docs make it look.


More information about the Digitalmars-d-learn mailing list