Jump to page: 1 2
Thread overview
returning struct, destructor
Dec 21, 2016
Eugene Wissner
Dec 21, 2016
Nicholas Wilson
Dec 21, 2016
Eugene Wissner
Dec 21, 2016
John C
Dec 21, 2016
evilrat
Dec 21, 2016
Eugene Wissner
Dec 21, 2016
kinke
Dec 22, 2016
Eugene Wissner
Dec 21, 2016
bauss
Dec 21, 2016
kinke
Dec 21, 2016
Ali Çehreli
December 21, 2016
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

December 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?
> Thx

Structs 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.

December 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:
>> 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
>
> Structs 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.

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 behavior
December 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.
December 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:
>> 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.

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();
}
December 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:
>> 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.

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.
December 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:
>> 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.
>
> 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.

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.
December 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:
>> 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.
>
> 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.

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.
December 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.
December 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

« First   ‹ Prev
1 2