Phobos 3 Discussion Notes - 02-01-2024

jmh530 john.michael.hall at gmail.com
Mon Feb 12 18:13:45 UTC 2024


On Monday, 12 February 2024 at 17:00:01 UTC, Atila Neves wrote:
> On Wednesday, 7 February 2024 at 21:26:51 UTC, jmh530 wrote:
>> On Wednesday, 7 February 2024 at 10:10:27 UTC, Atila Neves 
>> wrote:
>>> [snip]
>>>
>>> The problem with this approach, as C++ found out, is that 
>>> `Vector!(int, MyAlloc)` is a different type from 
>>> `Vector!(int, YourAlloc)`. The way I got around that is by 
>>> defaulting to a global allocator. This has its drawbacks as 
>>> well of course, because now everything is a virtual call.
>>
>> If you need to allocate or deallocate, then it's useful to 
>> treat them as separate types. Otherwise it's not.
>
>     alias MyVec = Vector(MyType, Mallocator);
>     void fun(MyVec vec) {
>          // just before the curly brace, the elements and 
> storage for them get deallocated.
>     }

I assume you mean `alias MyVec = Vector!(MyType, Mallocator);`.

I'm not completely sure I'm getting to the heart of what you're 
saying since your post is a bit sparse...but I'm doing my best.

Your `fun` function only works for `MyVec`, which depends on 
`MyType` and `Mallocator`. It isn't general across either types 
or allocators. It's kind of the essence of your complaint about 
C++ and treating `Vector!(int, MyAlloc)` and `Vector!(int, 
YourAlloc)` as different types, except you are pushing all the 
work downstream to functions. In other words, you have to have 
separate `fun` for all the different types and allocators you 
pass, and then have different allocation strategies for each. 
Multiply that across all the other functions.

If all the allocators are consistent with the interface from 
`std.experimental.allocator`, then you might be able to make the 
allocation more general. But, it becomes frustrating to rely on 
template aliases. The compiler can't see through them due to 
issue 1807 [1]. The more general approach is to use template 
constraints (see example below). The problem with the template 
constraint approach is that it leads to template bloat, which is 
why I suggested the `alias this` approach above.

Another approach would be to pass the allocator to the function 
instead of making it part of the type. The problem with this is 
that for some allocators you need to know what did the allocating 
in order to free it safely.

```d
import std.stdio;

struct Vector(T, U) { T x; U y;}
alias MyVec = Vector!(double, int);
void fun(MyVec vec)
{
     writeln("works");
}
alias MyVec2(T) = Vector!(T, int);
void fun2(T)(MyVec2!T vec)
{
     writeln("works2");
}
void fun3(T)(T vec)
     if (is(T : Vector!(U, int), U))
{
     writeln("works3");
}

void main() {
     MyVec vec = MyVec(1.0, 1);
     fun(vec); // prints works
     //fun2(vec); //error
     fun2!double(vec); // prints works2
     fun3(vec); // prints works3
}
```

[1] https://issues.dlang.org/show_bug.cgi?id=1807


More information about the Digitalmars-d mailing list