Scope of temporaries as function arguments

monarch_dodra monarchdodra at gmail.com
Fri Jun 28 09:01:04 PDT 2013


On Friday, 28 June 2013 at 15:33:40 UTC, Maxim Fomin wrote:
> On Friday, 28 June 2013 at 15:17:12 UTC, monarch_dodra wrote:
>>
>> Should I have expected a different behavior?
>
> import std.stdio;
>
> int callme()
> {
>    throw new Exception("");
> }
>
> struct S
> {
>    int i = 0;
>     this(int i){this.i = i; writeln("constructing: ", i);}
>     this(this){writeln("postbliting: ", i);}
>     ~this(){writeln("destroying: ", i);}
> }
>
> void foo(S s, int i)
> {
>    s.i = 2;
> }
>
> void main()
> {
>    S s = S(1);
>    foo(s, callme());
> }
>
> Destructor for copied object is not called because it is placed 
> in foo(). Before calling foo(), dmd makes a copy of main.s, 
> calls postblit, then puts code to invoke callme() and code to 
> invoke foo(). Since callme() throws, foo() is not called and 
> destructor placed in foo() is also not called. A struct copy 
> escapes destructor.
>
> Now, if you try fix this by putting dtor for copy not in foo(), 
> but in main immediately after foo() invocation, you will have a 
> problem because destructor would get S(1) object while it 
> should destroy S(2). Any modification made in foo() is lost. 
> This can be possible fixed by passing copy by reference which 
> would probably create new ABI problems.

I thought that was where you were getting to. Couldn't this 
simply be solved by having the *caller*, destroy the object that 
was postblitted into foo? Since foo ends up not being called 
(because of the exception), then I see no problem having the 
caller destroy the "to-be-passed-but-ends-up-not" object?

Basically, it would mean creating a "argument scope" into which 
each arg is constructed. If something throws, then the "up to now 
built args" are deconstruted just like with standard scope. If 
you reach the end of the scope, then call is made, but passing 
the resposability of destruction to foo.

EG, pseudo code:

memcpy_all_args;
try
{
     foreach arg in args:
         arg.postblit;
         exit(failure) arg.destroy;
}
call_foo;

Isn't that how C++ does it? In terms of passing args by value, I 
see no difference between CC and postblit... And I'm 99% sure C++ 
doesn't have this problem...


More information about the Digitalmars-d-learn mailing list