The problem that took him 5 years to fix in C++, I solved in a minute with D

Timon Gehr timon.gehr at gmx.ch
Fri Mar 11 04:57:11 UTC 2022


On 10.03.22 12:52, Paul Backus wrote:
> 
> Reminds me of this CppCon talk by Andrei Alexandrescu that was posted in 
> the community Discord recently:
> 
> https://youtu.be/va9I2qivBOA

Nice talk, but the explanation why there is a quadratic number of 
template instantiations for `destroyLog` seems to be wrong.

`destroyLog` itself is actually only instantiated a linear number of 
times. In particular, it is not true that it is instantiated with every 
combination of `lo` and `hi` where `0≤lo<hi≤n`. Most of them are never 
reached.

E.g., n=8, recursion tree of (lo,hi)-arguments:

```
                      (0,8)
                /               \
          (0,4)                   (4,8)
         /     \                 /     \
    (0,2)       (2,4)       (4,6)       (6,8)
    /   \       /   \       /   \       /   \
(0,1) (1,2) (2,3) (3,4) (4,5) (5,6) (6,7) (7,8)
```

Of course, the number of instantiated templates is indeed quadratic 
because each static index operation instantiates a linear number of 
templates and there are n index operations.

There is also something weird going on with the computation of the 
middle point:

```c++
constexpr size_t m = lo + (hi + low)/2;
```

This should be

```c++
constexpr unsigned m = lo + (hi - lo)/2;
```

(Or just (lo + hi)/2, I highly doubt this is ever going to wrap around 
:o). )

The following glorious hack avoids the linear indexing by consuming each 
element in the type sequence exactly once, when it is needed. The 
template parameter Ts is a type_sequence and the automatically deducted 
function return type is Ts[hi - lo .. $].

This only causes a linear number of template instantiations.

```c++
template<unsigned lo,unsigned hi,typename Ts>
auto destroyLog(unsigned i,void *p){
     static_assert(lo < hi);
     assert(lo <= i && i < hi);
     if constexpr (lo + 1 != hi) {
         constexpr unsigned m = lo + (hi - lo)/2;
         using Rs = decltype(destroyLog<lo, m, Ts>(i, p));
         if (i < m){
             destroyLog<lo, m, Ts>(i, p);
             return decltype(destroyLog<m, hi, Rs>(i, p))();
         } else {
             return destroyLog<m, hi, Rs>(i, p);
         }
     } else{
         using T = typename head<Ts>::type;
         static_cast<T*>(p)->~T();
         return typename tail<Ts>::type();
     }
}
```


More information about the Digitalmars-d mailing list