On Saturday, 25 February 2017 at 13:14:24 UTC, Moritz Maxeiner
wrote:
On Saturday, 25 February 2017 at 10:44:07 UTC, Radu wrote:
On Saturday, 25 February 2017 at 08:36:02 UTC, Ali Çehreli
wrote:
On 02/25/2017 12:17 AM, Radu wrote:
> destroy(cc) -> does c = C.init
> destroy(*cc); -> calls the C dtor
>
> Is this by design? If so - how can I destroy and get the
> dtor
called
> without dereferencing the pointer?
It's by design because setting a pointer to null can be
considered as destroying the pointer. Dereferencing is the
right way of destroying the object through the pointer.
I had added the following warning after somebody else was
burnt by this feature. :)
http://ddili.org/ders/d.en/memory.html#ix_memory.destroy
Ali
I think this is BAD. Why?
- it is one of those WAT?? moments that brings a RTFM slap to
you. The defaults should not be surprising, and in this case
straight dangerous as it can lead to leaks.
Unfortunately, I don't think it's viable to change destroy (see
below), it would probably be better to cover this in the dlang
tour (if it isn't already).
- it is not always possible to dereference the pointer, think
some circular structures where deref would get you one of
those fwd. declaration errors.
In the interest of learning, could you provide an example of
such a case?
- the deprecated delete will call the dtor, destroy is suppose
to replace delete - hence it should work the same.
AFAIK destroy isn't supposed to replace delete, since delete is
destruction+deallocation and destroy is only destruction; and
by that definition they cannot work the same: AFAIR multiple
deletes are illegal (since that equals use after free), whereas
destroy can be used on the same object as often as you want
(the destructor will only be called the first time).
In my opinion destroy should do this:
- call dtor if the pointer type has one defined
- nullify the pointer
This is what I was expecting anyhow to happen...
This change would be backwards-incompatible and breaks user
code, especially manual memory management:
---
struct A {}
auto a = cast (A*) malloc(A.sizeof); // Allocate
emplace(a, 42); // Construct
destroy(a); // Destruct
free(a); // Deallocate
---
if destroy were to already nullify a, how were one supposed to
deallocate a?
Here is sample on how destroy fails with a fwd decl error:
struct A
{
B b;
C c;
}
struct B
{
Wrap!A val;
}
struct C
{
Wrap!A val;
}
struct Wrap(T)
{
this(bool b)
{
t = cast(T*) malloc(T.sizeof);
}
~this()
{
destroy(*t); // Error: struct app.A no size because of
forward reference
}
T* t;
}
Manual management fails now with the current construct, inst't it?
auto a = cast (A*) malloc(A.sizeof); // Allocate
emplace(a, 42); // Construct
destroy(a); // Destruct
|-- here a becomes null
assert(a is null); // :}
free(a); // Deallocate
|- free null...
You need to save a into a temp, then call free on temp.
A nice to have enhancement would be to return the destroyed
pointer from destroy, enabling something like:
destroy(a).free();