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