Getting code in strings: __EXPRESSION_OF__

Quirin Schroll qs.il.paperinik at gmail.com
Tue Jul 18 13:33:49 UTC 2023


There are special keywords that decay into context-dependent 
strings or values, e.g. `__FILE__` and `__LINE__` which can be 
used as default arguments and make logging a breeze.

For asserting, it would be nice to have access to the expression 
that was asserted on.

My idea was to add `__EXPRESSIONS_OF__` which decays to an array 
literal that contains the tokens as strings of selected function 
arguments. There’s two ways it can be used:
* `__EXPRESSIONS_OF__(`*comma-separated `Identifier`s*`)` to 
refer to (named) arguments.
* `__EXPRESSIONS_OF__(`*comma-separated 
`ConditionalExpression`s*`)` to index (0-based) into the 
arguments. Of course, the number must be less than the number of 
arguments passed. (The `ConditionalExpression` must evaluate to 
an integer at compile-time.)

Of course, if the list is of known length (which in ordinary 
code, it is), the resulting literal can be bound to a fixed-size 
array. As an example:
```d
void myAssert(bool condition, lazy string message = "assert 
failed: ", string[1] expression = __EXPRESSIONS_OF__(condition))
{ … }
```

The by-index referral is intended to make meta-programming 
easier, to have support for the case where the parameter isn’t 
really named (for whatever reason, which there are many, from 
linters not liking named parameters that aren’t used, to 
expansion of compile-time sequences).

The result is available at compile-time and can be bound to a 
template argument:

```d
void someTemplate(Ts..., Es = __EXPRESSIONS_OF__(args))(Ts args) 
{ … }
void someTemplate(Ts...)(Ts args, string[1] expr = 
__EXPRESSIONS_OF__(0)) { … }
```

What do you think?

To a limited extent, it can be replicated using `alias` template 
parameters:
```d
void assertEquals(alias value, T)(T expected)
{
     if (value != expected)
     {
         import std.stdio;
         writeln(value.stringof, " had an unexpected value: ", 
value, " != ", expected);
     }
}

void main()
{
     int x = 10;
     assertEquals!x(10); // silent
     assertEquals!x(11); // prints: x had an unexpected value: 10 
!= 11
}
```
However, that only works with identifiers.


More information about the Digitalmars-d mailing list