Re: Silicon Valley D Meetup - March 18, 2021 - "Templates in the D Programming Language" by Ali Çehreli
Ali Çehreli
acehreli at yahoo.com
Fri Mar 19 17:10:27 UTC 2021
On 3/19/21 2:45 AM, data pulverizer wrote:
> I have to say that I got an enormous amount out of it!
Thank you for attending and steering the discussion to interesting
places. It turned out to be much different from a "beginner-friendly"
presentation. :)
It was interesting to understand some code by Andrei:
https://gist.github.com/andralex/0d85df38be2d9ffbe89cf1fb51c44213#file-reify-d-L164
It was interesting to realize that the selected line above can be an
example of a use case for nested templates. Instead of the following
line where the first template parameter is implicitly the "needle" and
the rest are the "haystack"
static assert(staticIndexOf!( byte, byte, short, int, long) == 0);
we can use nested templates to have more readable code:
static assert(staticIndexOf!byte.among!(byte, short, int, long) == 0);
(Assuming 'among' is a nested template inside 'staticIndexOf'.)
> I feel energised for what I learned.
It is too nerdy to say but I could not go to sleep easily due to some
adrenaline rush! Ha ha! :D
> There was one other person who made a useful comment that
> helped my understanding whose name I don't recall - thank you
> too.
That must be Jon Degenhardt. Jon mentioned how PR 7678 reduced the
performance of std.regex.matchOnce. After analyzing the code we realized
that the performance loss must be due to two delegate context allocations:
https://github.com/dlang/phobos/pull/7678/files#diff-269abc020de3a951eaaa5b8eca5a0700ba8b298767c7a64f459e74e1531a80aeR825
One delegate is 'matchOnceImp' and the other one is the anonymous
delegate created on the return expression.
We understood that 'matchOnceImp' could not be a nested function because
of an otherwise useful rule: the name of the nested function alone would
*call* that function instead of being a symbol for it. That is not the
case for a local delegate variable, so that's why 'matchOnceImp' exists
as a delegate variable there.
Then there is the addition of the 'pure' attribute to it. Fine...
After tinkering with the code, we realized that the same effect can be
achieved with a static member function of a static struct, which would
not allocate any delegate context. I add @nogc to the following code to
prove that point. The following code is even simpler than Jon and I came
up with yesterday.
void foo(ref int i) pure @nogc @safe {
// Another local variable that will be used by the static
// function of the static struct
double d = 0.0;
/* Instead of a lambda variable like this:
auto bar = () @trusted {
// ...
};
We use a static member function of a static struct:
*/
static struct S {
static auto bar(ref int j, double e) pure @trusted @nogc {
// Doing something unsafe inside this trusted function
*(cast(int*)cast(long*)&j) += 42 + e;
// Totally unrelated: It is a shame that the 'double'
// expression on the right-hand side is added to an 'int'
// on the left-hand side but we know that. :/
}
}
// Pass needed variables instead of using them from a delegate
// context
return (&S.bar)(i, d);
/* The following more convoluted method works as well but seems
* unnecessary:
alias T = typeof(&S.bar);
enum attrs = functionAttributes!T | FunctionAttribute.pure_ ;
return (() @trusted @nogc => (cast(SetFunctionAttributes!(T,
functionLinkage!T, attrs)) &S.bar))()(i, d);
*/
}
void main() {
int i = 0;
foo(i);
assert(i == 42);
}
There: we injected @trusted code inside a @nogc @safe function.
Question to others: Did we understand the reason for the convoluted code
in that PR fully? Is the above method really a better solution?
Ali
More information about the Digitalmars-d-announce
mailing list