Re: Why dtor are not executed when removing a struct from associative arrays?

2021-09-21 Thread Steven Schveighoffer via Digitalmars-d-learn

On 9/21/21 2:06 AM, Tejas wrote:

On Monday, 20 September 2021 at 18:13:53 UTC, Steven Schveighoffer wrote:

On 9/20/21 10:22 AM, Tejas wrote:
In case you still want to delete stuff deterministically despite what 
Steve said, I suggest you make your `struct` a reference and use 
`core.memory.__delete`(not recommended to use this carelessly, btw)


Do not call `__delete` here, use `destroy`. `__delete` will attempt to 
deallocate the block, which likely will fail since the key comes 
before the value, and GC.free on an interior pointer (I think) fails.


But if it succeeded, it would not be good. This leaves a dangling 
pointer inside the AA. Rehashing the AA likely would result in a 
memory corruption.


If you use destroy, the destructor will be called by the GC as well, 
but a struct should properly handle destroying the .init value.




It doesn't succeed when object is stack allocated, otherwise it works; 
that's why I suggested changing `S` to `S*`.


Oh! I missed that subtle change. I thought you were deleting a pointer 
to S that lives in a `S[int]`.


I still recommend against this because you can easily get a dangling 
pointer that way. Aside from that, if you are careful enough not to 
store a retrieved S* from the aa, you could do that. It will incur one 
extra allocation per element, which is not ideal.


Using destroy should be the most effective and safest mechanism.

-Steve


Re: Why dtor are not executed when removing a struct from associative arrays?

2021-09-21 Thread Tejas via Digitalmars-d-learn
On Monday, 20 September 2021 at 18:13:53 UTC, Steven 
Schveighoffer wrote:

On 9/20/21 10:22 AM, Tejas wrote:
In case you still want to delete stuff deterministically 
despite what Steve said, I suggest you make your `struct` a 
reference and use `core.memory.__delete`(not recommended to 
use this carelessly, btw)


Do not call `__delete` here, use `destroy`. `__delete` will 
attempt to deallocate the block, which likely will fail since 
the key comes before the value, and GC.free on an interior 
pointer (I think) fails.


But if it succeeded, it would not be good. This leaves a 
dangling pointer inside the AA. Rehashing the AA likely would 
result in a memory corruption.


If you use destroy, the destructor will be called by the GC as 
well, but a struct should properly handle destroying the .init 
value.


-Steve


It doesn't succeed when object is stack allocated, otherwise it 
works; that's why I suggested changing `S` to `S*`.


As for why I didn't use `destroy`, I assumed OP wanted to free 
the memory. If they just want the destructor to run, then 
`destroy` is better(and safer).


(Also, `hashOf` seems to work just fine even after using 
`__delete`, not that it means using `__delete` should ever be 
recommended over using `destroy`)


```d
import std.stdio;
import core.memory:__delete;

struct S
{
int a;
this(int param){
a = param;
}
~this()
{
writeln("S(",a,") is being destructed");
}
}

void absoluteRemove(AA, keyType)(AA assArray, keyType key){
//assArray.remove(key);
__delete(assArray[key]);
}


void main()
{
S*[int] aa;
aa[1]  = new S(5);
absoluteRemove(aa, 1);
writeln(aa.hashOf);
aa[2] = new S(10);
writeln(aa.hashOf);
absoluteRemove(aa, 2);
writeln(aa.hashOf);
writeln("Why no dtor call on remove?");
}



Output:
S(5) is being destructed
2451737883
5104465521
S(10) is being destructed
5378492086
Why no dtor call on remove?
```


Re: Why dtor are not executed when removing a struct from associative arrays?

2021-09-20 Thread Steven Schveighoffer via Digitalmars-d-learn

On 9/20/21 10:22 AM, Tejas wrote:
In case you still want to delete stuff deterministically despite what 
Steve said, I suggest you make your `struct` a reference and use 
`core.memory.__delete`(not recommended to use this carelessly, btw)


Do not call `__delete` here, use `destroy`. `__delete` will attempt to 
deallocate the block, which likely will fail since the key comes before 
the value, and GC.free on an interior pointer (I think) fails.


But if it succeeded, it would not be good. This leaves a dangling 
pointer inside the AA. Rehashing the AA likely would result in a memory 
corruption.


If you use destroy, the destructor will be called by the GC as well, but 
a struct should properly handle destroying the .init value.


-Steve


Re: Why dtor are not executed when removing a struct from associative arrays?

2021-09-20 Thread Ali Çehreli via Digitalmars-d-learn

On 9/20/21 5:23 AM, Learner wrote:


I was expecting S instance dtor called
S is being destructed


If you are sure the element can be destroyed, you can call destroy():

import std.stdio;

enum someSpecialInitValue = 777;

struct S
{
  int i = someSpecialInitValue;

  this(int i)
  {
this.i = i;
  }

  ~this()
  {
writeln("S with ", i, " is being destructed");
  }
}

void destroyAndRemove(AA, Key)(AA aa, Key key) {
  auto found = key in aa;
  if (found) {
destroy(*found);
aa.remove(key);
  }
}

void main()
{
  S[int] aa;
  aa[1] = S(1);
  aa.destroyAndRemove(1);
  writeln("Actually, 2 dtor calls! :)");
}

destroy() puts the object to its initial state, which means, it gets 
destroyed again. That's why there are two destructor calls below for the 
same object. I used a special value to demonstrate the second destructor 
is called on the init state:


S with 1 is being destructed
Actually, 2 dtor calls! :)
S with 777 is being destructed

Ali


Re: Why dtor are not executed when removing a struct from associative arrays?

2021-09-20 Thread Tejas via Digitalmars-d-learn

On Monday, 20 September 2021 at 14:03:09 UTC, Tejas wrote:

On Monday, 20 September 2021 at 13:48:01 UTC, Tejas wrote:

On Monday, 20 September 2021 at 12:23:00 UTC, Learner wrote:

[...]


I think it *is* being called:
```d
import std.stdio;

struct S
{
int a;
this(int param){
a = param;
}
~this()
{
writeln("S(",a,") is being destructed");
}
}

void main()
{
S[int] aa;
aa[1] = S(5);
aa.remove(1);
aa[1] = S(10);
//writeln("Why no dtor call on remove?");
}

Output:
S(5) is being destructed
S(10) is being destructed
```
If  the destructors were being executed based on the ending of 
scope, `S(10)` would have been destroyed first.


Oh dear, I made a mistake :(
Sorry, shouldn't have commented out the `writeln`


To make up for my mistake:

In case you still want to delete stuff deterministically despite 
what Steve said, I suggest you make your `struct` a reference and 
use `core.memory.__delete`(not recommended to use this 
carelessly, btw)

```d
import std.stdio;
import core.memory:__delete;

struct S
{
int a;
this(int param){
a = param;
}
~this()
{
writeln("S(",a,") is being destructed");
}
}

void absoluteRemove(AA, keyType)(AA assArray, keyType key){
__delete(assArray[key]);
}


void main()
{
S*[int] aa;
aa[1]  = new S(5);
absoluteRemove(aa, 1);
aa[2] = new S(10);
absoluteRemove(aa, 2);
writeln("Why no dtor call on remove?");
}
Output:
S(5) is being destructed
S(10) is being destructed
Why no dtor call on remove?
```

Because `__delete` cannot work on stack allocated objects


Re: Why dtor are not executed when removing a struct from associative arrays?

2021-09-20 Thread Tejas via Digitalmars-d-learn

On Monday, 20 September 2021 at 13:48:01 UTC, Tejas wrote:

On Monday, 20 September 2021 at 12:23:00 UTC, Learner wrote:

[...]


I think it *is* being called:
```d
import std.stdio;

struct S
{
int a;
this(int param){
a = param;
}
~this()
{
writeln("S(",a,") is being destructed");
}
}

void main()
{
S[int] aa;
aa[1] = S(5);
aa.remove(1);
aa[1] = S(10);
//writeln("Why no dtor call on remove?");
}

Output:
S(5) is being destructed
S(10) is being destructed
```
If  the destructors were being executed based on the ending of 
scope, `S(10)` would have been destroyed first.


Oh dear, I made a mistake :(
Sorry, shouldn't have commented out the `writeln`


Re: Why dtor are not executed when removing a struct from associative arrays?

2021-09-20 Thread Steven Schveighoffer via Digitalmars-d-learn

On 9/20/21 8:23 AM, Learner wrote:

I was expecting something like going out of scope for that

```d
import std.stdio;

struct S
{
     ~this()
     {
     writeln("S is being destructed");
     }
}

void main()
{
 S[int] aa;
     aa[1] = S();
     aa.remove(1);
     writeln("Why no dtor call on remove?");
}
```
I was expecting S instance dtor called
S is being destructed


AA values are not destroyed on removal. For a simple reason -- someone 
might still be referencing it.


```d
struct S
{
   int x;
}
void main()
{
   S[int] aa;
   aa[1] = S(5);
   auto sptr = 1 in aa;
   sptr.x = 6;
   assert(aa[1].x == 6);
   aa.remove(1);
   assert(sptr.x == 6;
}
```

-Steve


Re: Why dtor are not executed when removing a struct from associative arrays?

2021-09-20 Thread jfondren via Digitalmars-d-learn

On Monday, 20 September 2021 at 12:23:00 UTC, Learner wrote:

I was expecting something like going out of scope for that

```(D)
import std.stdio;

struct S
{
~this()
{
writeln("S is being destructed");
}
}

void main()
{
S[int] aa;
aa[1] = S();
aa.remove(1);
writeln("Why no dtor call on remove?");
}

I was expecting S instance dtor called
S is being destructed
```


This looks to me like a bug, as

```d
import core.memory : GC;
GC.collect;
```

immediately after the `.remove` will call the struct's destructor.

I only see https://issues.dlang.org/show_bug.cgi?id=20379 as 
related, though.


Here's another workaround:

```d
alias purge = (kv, k) { kv[k] = typeof(kv[k]).init; kv.remove(k); 
};

```

with the same caveat of the `.init` structs also getting 
destructed later, you can use that in place of `.remove`.


Re: Why dtor are not executed when removing a struct from associative arrays?

2021-09-20 Thread Tejas via Digitalmars-d-learn

On Monday, 20 September 2021 at 12:23:00 UTC, Learner wrote:

I was expecting something like going out of scope for that

```(D)
import std.stdio;

struct S
{
~this()
{
writeln("S is being destructed");
}
}

void main()
{
S[int] aa;
aa[1] = S();
aa.remove(1);
writeln("Why no dtor call on remove?");
}

I was expecting S instance dtor called
S is being destructed
```


I think it *is* being called:
```d
import std.stdio;

struct S
{
int a;
this(int param){
a = param;
}
~this()
{
writeln("S(",a,") is being destructed");
}
}

void main()
{
S[int] aa;
aa[1] = S(5);
aa.remove(1);
aa[1] = S(10);
//writeln("Why no dtor call on remove?");
}

Output:
S(5) is being destructed
S(10) is being destructed
```
If  the destructors were being executed based on the ending of 
scope, `S(10)` would have been destroyed first.


Why dtor are not executed when removing a struct from associative arrays?

2021-09-20 Thread Learner via Digitalmars-d-learn

I was expecting something like going out of scope for that

```(D)
import std.stdio;

struct S
{
~this()
{
writeln("S is being destructed");
}
}

void main()
{
S[int] aa;
aa[1] = S();
aa.remove(1);
writeln("Why no dtor call on remove?");
}

I was expecting S instance dtor called
S is being destructed
```