digitalmars.D.learn - returning struct, destructor
- Eugene Wissner (21/21) Dec 21 2016 Consider we have a function that returns a struct. So for example:
- Nicholas Wilson (7/29) Dec 21 2016 Structs are value types, so unless they you pass them by
- Eugene Wissner (6/40) Dec 21 2016 Why if the "if block" is removed, the code prints "Destruct" only
- John C (3/6) Dec 21 2016 Possibly to do with named return value optimisation.
- evilrat (32/38) Dec 21 2016 looks like it is really optimized away. an "improved" test case,
- Eugene Wissner (4/10) Dec 21 2016 Isn't an optimization that changes the behavior bad? I had a
- kinke (13/24) Dec 21 2016 Basic stuff such as this is appropriately tested. The named
- Eugene Wissner (4/15) Dec 21 2016 Thanks a lot. It makes sense. It seemed just weired that a
- bauss (6/17) Dec 21 2016 It isn't an optimization that changes behavior.
- kinke (3/6) Dec 21 2016 That's incorrect, it doesn't have anything to do with the return
- =?UTF-8?Q?Ali_=c3=87ehreli?= (7/10) Dec 21 2016 Sounds like you ended up with two objects that owned the same resource.
Consider we have a function that returns a struct. So for example: import std.stdio; struct A { ~this() { writeln("Destruct"); } } A myFunc() { auto a = A(), b = A(); if (false) { return a; } return b; } void main() { myFunc(); } This prints 3 times "Destruct" with dmd 0.072.1. If I remove the if block, it prints "Destruct" only 2 times - the behavior I'm expecting. Why? Thx
Dec 21 2016
On Wednesday, 21 December 2016 at 11:45:18 UTC, Eugene Wissner wrote:Consider we have a function that returns a struct. So for example: import std.stdio; struct A { ~this() { writeln("Destruct"); } } A myFunc() { auto a = A(), b = A(); if (false) { return a; } return b; } void main() { myFunc(); } This prints 3 times "Destruct" with dmd 0.072.1. If I remove the if block, it prints "Destruct" only 2 times - the behavior I'm expecting. Why? ThxStructs are value types, so unless they you pass them by pointer/reference, they get copied. in myFunc it prints "Destruct" twice, one for a and once for b. In main it prints it one more for the (discarded) A returned from myfunc.
Dec 21 2016
On Wednesday, 21 December 2016 at 12:32:51 UTC, Nicholas Wilson wrote:On Wednesday, 21 December 2016 at 11:45:18 UTC, Eugene Wissner wrote:Why if the "if block" is removed, the code prints "Destruct" only two times. One because "a" goes out of scope and one in the main function. I don't understand, why "if (false) ..." changes the behaviorConsider we have a function that returns a struct. So for example: import std.stdio; struct A { ~this() { writeln("Destruct"); } } A myFunc() { auto a = A(), b = A(); if (false) { return a; } return b; } void main() { myFunc(); } This prints 3 times "Destruct" with dmd 0.072.1. If I remove the if block, it prints "Destruct" only 2 times - the behavior I'm expecting. Why? ThxStructs are value types, so unless they you pass them by pointer/reference, they get copied. in myFunc it prints "Destruct" twice, one for a and once for b. In main it prints it one more for the (discarded) A returned from myfunc.
Dec 21 2016
On Wednesday, 21 December 2016 at 11:45:18 UTC, Eugene Wissner wrote:This prints 3 times "Destruct" with dmd 0.072.1. If I remove the if block, it prints "Destruct" only 2 times - the behavior I'm expecting. Why?Possibly to do with named return value optimisation.
Dec 21 2016
On Wednesday, 21 December 2016 at 14:15:06 UTC, John C wrote:On Wednesday, 21 December 2016 at 11:45:18 UTC, Eugene Wissner wrote:looks like it is really optimized away. an "improved" test case, even with "if" block removed or changed to true now it always the same number of operations ========================================== import std.stdio; import std.conv : to; struct A { int num; this(int n) { num = n; } this(const ref A other) { num = -1; // special case writeln("copy of " ~ to!string(other.num)); } ~this() { writeln("Destruct" ~ to!string(num)); } } A myFunc() { auto a = A(1), b = A(2); if (false) { return A(a); } return A(b); } void main() { myFunc(); }This prints 3 times "Destruct" with dmd 0.072.1. If I remove the if block, it prints "Destruct" only 2 times - the behavior I'm expecting. Why?Possibly to do with named return value optimisation.
Dec 21 2016
On Wednesday, 21 December 2016 at 14:15:06 UTC, John C wrote:On Wednesday, 21 December 2016 at 11:45:18 UTC, Eugene Wissner wrote:Isn't an optimization that changes the behavior bad? I had a crash in the code where the destructor did something meaningfull, freed the memory (the same pointer) twice.This prints 3 times "Destruct" with dmd 0.072.1. If I remove the if block, it prints "Destruct" only 2 times - the behavior I'm expecting. Why?Possibly to do with named return value optimisation.
Dec 21 2016
On Wednesday, 21 December 2016 at 15:01:20 UTC, Eugene Wissner wrote:On Wednesday, 21 December 2016 at 14:15:06 UTC, John C wrote:Basic stuff such as this is appropriately tested. The named return value optimization is enforced by D (incl. unoptimized builds), so behavior doesn't change by this optimization. It's you who changed the behavior by removing the if. Due to the `if`, the compiler doesn't know whether it should construct `a` or `b` directly into the memory (sret pointee) provided by the caller. When omitting the `if`, it's clear that `b` is returned in all cases, so the compiles constructs `a` on the local stack (and destructs it before exiting the function), but emplaces `b` into `*sret` (i.e., the caller's stack) and can thus elide 1x postblit + 1x dtor.On Wednesday, 21 December 2016 at 11:45:18 UTC, Eugene Wissner wrote:Isn't an optimization that changes the behavior bad? I had a crash in the code where the destructor did something meaningfull, freed the memory (the same pointer) twice.This prints 3 times "Destruct" with dmd 0.072.1. If I remove the if block, it prints "Destruct" only 2 times - the behavior I'm expecting. Why?Possibly to do with named return value optimisation.
Dec 21 2016
On Wednesday, 21 December 2016 at 17:49:22 UTC, kinke wrote:Basic stuff such as this is appropriately tested. The named return value optimization is enforced by D (incl. unoptimized builds), so behavior doesn't change by this optimization. It's you who changed the behavior by removing the if. Due to the `if`, the compiler doesn't know whether it should construct `a` or `b` directly into the memory (sret pointee) provided by the caller. When omitting the `if`, it's clear that `b` is returned in all cases, so the compiles constructs `a` on the local stack (and destructs it before exiting the function), but emplaces `b` into `*sret` (i.e., the caller's stack) and can thus elide 1x postblit + 1x dtor.Thanks a lot. It makes sense. It seemed just weired that a conditional return value causes such a change. But I begin to understand the background.
Dec 21 2016
On Wednesday, 21 December 2016 at 15:01:20 UTC, Eugene Wissner wrote:On Wednesday, 21 December 2016 at 14:15:06 UTC, John C wrote:It isn't an optimization that changes behavior. It removes an unnecessary allocation for the returning copy of the struct, as the return value is never used. Hence why it's pointless that it would be compiled anyway.On Wednesday, 21 December 2016 at 11:45:18 UTC, Eugene Wissner wrote:Isn't an optimization that changes the behavior bad? I had a crash in the code where the destructor did something meaningfull, freed the memory (the same pointer) twice.This prints 3 times "Destruct" with dmd 0.072.1. If I remove the if block, it prints "Destruct" only 2 times - the behavior I'm expecting. Why?Possibly to do with named return value optimisation.
Dec 21 2016
On Wednesday, 21 December 2016 at 18:02:54 UTC, bauss wrote:It removes an unnecessary allocation for the returning copy of the struct, as the return value is never used. Hence why it's pointless that it would be compiled anyway.That's incorrect, it doesn't have anything to do with the return value being used or not.
Dec 21 2016
On 12/21/2016 07:01 AM, Eugene Wissner wrote:Isn't an optimization that changes the behavior bad? I had a crash in the code where the destructor did something meaningfull, freed the memory (the same pointer) twice.Sounds like you ended up with two objects that owned the same resource. You can either ' disable this(this)' so that copies of this type are not allowed or you can handle the resource in a different way: Make a copy of the resource in this(this) so the two copies have their own separate resources, leave the resource to the GC, use reference counting, etc. Ali
Dec 21 2016