www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - scope(exit) without exception handling?

reply "Mehrdad" <wfunction hotmail.com> writes:
I'm writing some (low-level) code with no exception handling 
available whatsoever... either the code runs, or it doesn't.

Is there any way for me to use scope(exit) (or perhaps a 
destructor, like RAII) to mean, "Execute this block of code for 
me when the block is exited, will ya?", *without* introducing 
dependencies on exception handling?
May 15 2012
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, May 16, 2012 05:54:04 Mehrdad wrote:
 I'm writing some (low-level) code with no exception handling
 available whatsoever... either the code runs, or it doesn't.
 
 Is there any way for me to use scope(exit) (or perhaps a
 destructor, like RAII) to mean, "Execute this block of code for
 me when the block is exited, will ya?", *without* introducing
 dependencies on exception handling?

scope(exit) stuff; otherStuff; is lowered to something like try { otherStuff; } finally { stuff; } So, you can use scope(exit) if the above code is acceptable for whatever you're doing. Otherwise, no, you can't. Destructors should work regardless of what you're doing with exceptions though, so I would expect RAII to work. - Jonathan M Davis
May 15 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Wednesday, 16 May 2012 at 05:06:51 UTC, Jonathan M Davis wrote:

 scope(exit) stuff;
 otherStuff;

 is lowered to something like

 try
 {
     otherStuff;
 }
 finally
 {
     stuff;
 }

 So, you can use scope(exit) if the above code is acceptable for 
 whatever you're doing. Otherwise, no, you can't.

Thanks, though I already knew that...
 Destructors should work regardless of what you're doing with 
 exceptions though, so I would expect RAII to work.

Well, RAII is pretty much just a finally block... It seems like all of these emit references _d_local_unwind2 and stuff, so it seems like it's not the way you expect...
May 15 2012
prev sibling next sibling parent reply Brad Roberts <braddr puremagic.com> writes:
On 5/15/2012 10:06 PM, Jonathan M Davis wrote:
 On Wednesday, May 16, 2012 05:54:04 Mehrdad wrote:
 I'm writing some (low-level) code with no exception handling
 available whatsoever... either the code runs, or it doesn't.

 Is there any way for me to use scope(exit) (or perhaps a
 destructor, like RAII) to mean, "Execute this block of code for
 me when the block is exited, will ya?", *without* introducing
 dependencies on exception handling?

scope(exit) stuff; otherStuff; is lowered to something like try { otherStuff; } finally { stuff; } So, you can use scope(exit) if the above code is acceptable for whatever you're doing. Otherwise, no, you can't. Destructors should work regardless of what you're doing with exceptions though, so I would expect RAII to work. - Jonathan M Davis

And if otherStuff is marked all nothrow, then the exception parts are pulled out. It's pretty much the entire point of having nothrow annotations.
May 15 2012
next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 16/05/2012 11:59, Trass3r a écrit :
 scope(exit) stuff;
 otherStuff;

 is lowered to something like

 try
 {
 otherStuff;
 }
 finally
 {
 stuff;
 }

pulled out. It's pretty much the entire point of having nothrow annotations.

This should be added to http://dlang.org/function.html#nothrow-functions

Except for thing throw by the runtime, which can basically occur at any moment.
May 16 2012
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 05/16/2012 11:09 PM, H. S. Teoh wrote:
 OK, this isn't the same as your nothrow wrapper, but the principle is
 the same. The funcWrap template can basically call _any_ function that
 returns _anything_.

 D just acquired whole new levels of cool for me. :-)

There are still some restrictions to be sorted out though. For example, try it with funcWrap(&printShort,1);
May 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Wednesday, 16 May 2012 at 05:39:08 UTC, Brad Roberts wrote:
 On 5/15/2012 10:06 PM, Jonathan M Davis wrote:
 On Wednesday, May 16, 2012 05:54:04 Mehrdad wrote:
 I'm writing some (low-level) code with no exception handling
 available whatsoever... either the code runs, or it doesn't.

 Is there any way for me to use scope(exit) (or perhaps a
 destructor, like RAII) to mean, "Execute this block of code 
 for
 me when the block is exited, will ya?", *without* introducing
 dependencies on exception handling?

scope(exit) stuff; otherStuff; is lowered to something like try { otherStuff; } finally { stuff; } So, you can use scope(exit) if the above code is acceptable for whatever you're doing. Otherwise, no, you can't. Destructors should work regardless of what you're doing with exceptions though, so I would expect RAII to work. - Jonathan M Davis

And if otherStuff is marked all nothrow, then the exception parts are pulled out. It's pretty much the entire point of having nothrow annotations.

Oooh, *that* I did not know. Very interesting, thanks for pointing that out!
May 15 2012
prev sibling next sibling parent "Robert DaSilva" <spunit262 yahoo.com> writes:
On Wednesday, 16 May 2012 at 05:46:03 UTC, Mehrdad wrote:=
 Oooh, *that* I did not know. Very interesting, thanks for 
 pointing that out!

You could try scope(success), but the nothrow annotations sound like a better idea
May 15 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, May 16, 2012 07:30:44 Mehrdad wrote:
 Well, RAII is pretty much just a finally block...
 It seems like all of these emit references _d_local_unwind2 and
 stuff, so it seems like it's not the way you expect...

It all depends on how it's implemented I guess. RAII doesn't directly have anything to do with exceptions (though it's a good way to write exception-safe code), so it would be perfectly possible to have it in a language with no exceptions at all, but I guess that it could be implemented in a manner similar to finally blocks, much as I wouldn't have expected it. I haven't looked at what exactly the compiler generates though, so if you've dug into that, you know more about it than I do. - Jonathan M Davis
May 15 2012
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 5/15/2012 8:54 PM, Mehrdad wrote:
 Is there any way for me to use scope(exit) (or perhaps a destructor, like RAII)
 to mean, "Execute this block of code for me when the block is exited, will
ya?",
 *without* introducing dependencies on exception handling?

Make sure the guarded code is 'nothrow', and it should work.
May 15 2012
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 05/16/2012 08:59 AM, Walter Bright wrote:
 On 5/15/2012 8:54 PM, Mehrdad wrote:
 Is there any way for me to use scope(exit) (or perhaps a destructor,
 like RAII)
 to mean, "Execute this block of code for me when the block is exited,
 will ya?",
 *without* introducing dependencies on exception handling?

Make sure the guarded code is 'nothrow', and it should work.

Doesn't that imply that 'in'-contract checking might leave the program in an invalid state?
May 16 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 05/16/2012 11:17 PM, Jonathan M Davis wrote:
 On Wednesday, May 16, 2012 23:05:07 Timon Gehr wrote:
 On 05/16/2012 08:59 AM, Walter Bright wrote:
 On 5/15/2012 8:54 PM, Mehrdad wrote:
 Is there any way for me to use scope(exit) (or perhaps a destructor,
 like RAII)
 to mean, "Execute this block of code for me when the block is exited,
 will ya?",
 *without* introducing dependencies on exception handling?

Make sure the guarded code is 'nothrow', and it should work.

Doesn't that imply that 'in'-contract checking might leave the program in an invalid state?

Well, according to Walter, there is no guarantee that _any_ cleanup will be done when an Error is thrown (including AssertError), so yes, there's the possibility that an in contract could leave the program in an invalid state if it fails.

The issue is not relevant for failing contracts. 'in' contracts might pass even if some assertion errors were thrown during their evaluation.
 However, with the current implementation, as I understand it, it
 _is_ guaranteed that cleanup will be done for Errors. But if the try and
 finally blocks are indeed removed, then that would seem to indicate that
 there's a case where there _won't_ be any cleaup for Errors in spite of the
 fact that Don (and and Dan?) tried to make sure that it _was_ guaranteed. But
 if the cleanup doesn't happen, it would still be within what Walter considers
 to be guarantee for Errors as far as the spec goes.

 - Jonathan M Davis

Exactly, this is somewhat bothersome.
May 16 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 16/05/2012 08:59, Walter Bright a écrit :
 On 5/15/2012 8:54 PM, Mehrdad wrote:
 Is there any way for me to use scope(exit) (or perhaps a destructor,
 like RAII)
 to mean, "Execute this block of code for me when the block is exited,
 will ya?",
 *without* introducing dependencies on exception handling?

Make sure the guarded code is 'nothrow', and it should work.

It doesn't right now, and exceptions can be thrown at any time basically. scope(success) seems like a better choice here, but I'm not sure of the generated code for it.
May 16 2012
prev sibling next sibling parent Trass3r <un known.com> writes:
 scope(exit) stuff;
 otherStuff;

 is lowered to something like

 try
 {
     otherStuff;
 }
 finally
 {
     stuff;
 }

pulled out. It's pretty much the entire point of having nothrow annotations.

This should be added to http://dlang.org/function.html#nothrow-functions
May 16 2012
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 15 May 2012 23:54:04 -0400, Mehrdad <wfunction hotmail.com> wrote:

 I'm writing some (low-level) code with no exception handling available  
 whatsoever... either the code runs, or it doesn't.

 Is there any way for me to use scope(exit) (or perhaps a destructor,  
 like RAII) to mean, "Execute this block of code for me when the block is  
 exited, will ya?", *without* introducing dependencies on exception  
 handling?

struct AddExitBlock { private void delegate() dg; this(scope void delegate() dg) {this.dg = dg;} ~this() {dg();} } void main() { int x = 0; { x = 1; immutable _aeb = AddExitBlock({x = 0;}); // annoying to have to assign it to a temporary variable, but whatever. assert(x == 1); } assert(x == 0); } Also seems to work with exceptions (if you need them): void foo(ref int x) { x = 1; immutable aeb1 = AddExitBlock({x = 0;}); throw new Exception("testing!"); } void main() { int x = 0; try { foo(x); } catch(Exception e) { } assert(x == 0); } I don't see exception handling in the generated code (at least I don't see the _d_local_unwind2), I wonder a) if this is more efficient than scope(exit), and b) if so, why can't the compiler do this automatically? -Steve
May 16 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-05-16 15:10, Steven Schveighoffer wrote:

 I don't see exception handling in the generated code (at least I don't
 see the _d_local_unwind2), I wonder a) if this is more efficient than
 scope(exit), and b) if so, why can't the compiler do this automatically?

I'm guessing because constructors and destructors hadn't been introduced for structs when the scope-statement was. -- /Jacob Carlborg
May 16 2012
prev sibling next sibling parent "David Nadlinger" <see klickverbot.at> writes:
On Wednesday, 16 May 2012 at 13:10:05 UTC, Steven Schveighoffer 
wrote:
 I don't see exception handling in the generated code (at least 
 I don't see the _d_local_unwind2), I wonder a) if this is more 
 efficient than scope(exit), and b) if so, why can't the 
 compiler do this automatically?

I think you might be misreading the assembly – which operating system are you on? You can only expect to see _d_local_unwind on Windows, Dwarf EH is implemented differently. In the first case, where the code can't throw, the exception handling code is probably not generated at all. David
May 16 2012
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 16 May 2012 13:19:01 -0400, David Nadlinger <see klickverbot.at>=
  =

wrote:

 On Wednesday, 16 May 2012 at 13:10:05 UTC, Steven Schveighoffer wrote:=

 I don't see exception handling in the generated code (at least I don'=


 see the _d_local_unwind2), I wonder a) if this is more efficient than=


 scope(exit), and b) if so, why can't the compiler do this automatical=


 I think you might be misreading the assembly =E2=80=93 which operating=

 are you on? You can only expect to see _d_local_unwind on Windows, Dwa=

 EH is implemented differently.

OK, that probably explains it :)
 In the first case, where the code can't throw, the exception handling =

 code is probably not generated at all.

Yes, I see that the compiler likely does the right thing, and I just = implemented a totally useless feature in the face of scope(exit) :) I was kind of curious though, if it would work! -Steve
May 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
Hmmm... when I remove the reference to SNN.lib, the code
	auto scoped() nothrow {
		struct S { ~this() { } }
		S s;
		return s;
	}
	void main() { auto s = scoped(); }
tells me
	Error 42: Symbol Undefined __d_framehandler
	Error 42: Symbol Undefined __except_list


But, when I change it to
	auto scoped() nothrow {
		struct S { ~this() { } }
		return S();
	}
it compiles fine.


Is this counted as a 'bug'? Or is it intentional? (Should I 
report it?)
May 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
Oh, and I just invented a most *lovely* cast:  :P

auto noThrow(T)(scope T function() t) nothrow
{ return (cast(T function() nothrow)t)(); }

auto noThrow(T)(scope T delegate() t) nothrow
{ return (cast(T delegate() nothrow)t)(); }
May 16 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, May 16, 2012 at 10:41:06PM +0200, Mehrdad wrote:
 Oh, and I just invented a most *lovely* cast:  :P
 
 auto noThrow(T)(scope T function() t) nothrow
 { return (cast(T function() nothrow)t)(); }
 
 auto noThrow(T)(scope T delegate() t) nothrow
 { return (cast(T delegate() nothrow)t)(); }

What about: auto noThrow(T,U...)(scope T function(U) t) nothrow { return (cast(T function(U) nothrow)t)(); } auto noThrow(T,U...)(scope T delegate(U) t) nothrow { return (cast(T delegate(U) nothrow)t)(); } ? (I've no idea if this actually works, but it does allow you to wrap almost _any_ function.) T -- Lawyer: (n.) An innocence-vending machine, the effectiveness of which depends on how much money is inserted.
May 16 2012
prev sibling next sibling parent "Mehrdad" <wfunction hotmail.com> writes:
On Wednesday, 16 May 2012 at 20:48:27 UTC, H. S. Teoh wrote:
 What about:

 	auto noThrow(T,U...)(scope T function(U) t) nothrow {
 		return (cast(T function(U) nothrow)t)();
 	}

 	auto noThrow(T,U...)(scope T delegate(U) t) nothrow {
 		return (cast(T delegate(U) nothrow)t)();
 	}

 ?

 (I've no idea if this actually works, but it does allow you to 
 wrap
 almost _any_ function.)


 T

Haha maybe, idk. I just wrote what I wrote so that I could use it like: noThrow({ // giant block of code }); to execute it as nothrow.
May 16 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, May 16, 2012 at 10:54:26PM +0200, Mehrdad wrote:
[...]
 Haha maybe, idk. I just wrote what I wrote so that I could use it
 like:
 
 noThrow({
 	// giant block of code
 });
 
 
 to execute it as nothrow.

Whoa, this code works: import std.math; import std.stdio; T funcWrap(T,U...)(scope T function(U) f, U args) { writeln("Calling wrapped function"); scope(exit) writeln("Wrapped function returned"); return f(args); } void printInt(int x) { writeln(x); } float computeFloat(float x, float y) { return x^^2 + y; } void main() { funcWrap(&printInt, 12345); writeln("Result is: ", funcWrap(&computeFloat, 3.0f, 1.5f)); funcWrap({ writeln("Inside an anonymous delegate"); }); funcWrap((int x) { writeln("Delegate with parameter: ", x); }, 100); } Output: Calling wrapped function 12345 Wrapped function returned Calling wrapped function Wrapped function returned Result is: 10.5 Calling wrapped function Inside an anonymous delegate Wrapped function returned Calling wrapped function Delegate with parameter: 100 Wrapped function returned OK, this isn't the same as your nothrow wrapper, but the principle is the same. The funcWrap template can basically call _any_ function that returns _anything_. D just acquired whole new levels of cool for me. :-) T -- ASCII stupid question, getty stupid ANSI.
May 16 2012
prev sibling next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, May 16, 2012 23:05:07 Timon Gehr wrote:
 On 05/16/2012 08:59 AM, Walter Bright wrote:
 On 5/15/2012 8:54 PM, Mehrdad wrote:
 Is there any way for me to use scope(exit) (or perhaps a destructor,
 like RAII)
 to mean, "Execute this block of code for me when the block is exited,
 will ya?",
 *without* introducing dependencies on exception handling?

Make sure the guarded code is 'nothrow', and it should work.

Doesn't that imply that 'in'-contract checking might leave the program in an invalid state?

Well, according to Walter, there is no guarantee that _any_ cleanup will be done when an Error is thrown (including AssertError), so yes, there's the possibility that an in contract could leave the program in an invalid state if it fails. However, with the current implementation, as I understand it, it _is_ guaranteed that cleanup will be done for Errors. But if the try and finally blocks are indeed removed, then that would seem to indicate that there's a case where there _won't_ be any cleaup for Errors in spite of the fact that Don (and and Dan?) tried to make sure that it _was_ guaranteed. But if the cleanup doesn't happen, it would still be within what Walter considers to be guarantee for Errors as far as the spec goes. - Jonathan M Davis
May 16 2012
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, May 16, 2012 at 11:34:18PM +0200, Timon Gehr wrote:
 On 05/16/2012 11:09 PM, H. S. Teoh wrote:
OK, this isn't the same as your nothrow wrapper, but the principle is
the same. The funcWrap template can basically call _any_ function
that returns _anything_.

D just acquired whole new levels of cool for me. :-)

There are still some restrictions to be sorted out though. For example, try it with funcWrap(&printShort,1);

Yes I noticed that compiler type inference didn't work in that case. This is one area I really hope will be improved soon. I kept running into this in the new AA implementation: assigning [1,2,3] to ubyte[] works, but passing [1,2,3] to a template automatically forces it into int[] even though the template body then tries to assign it to a ubyte[], causing an error. Somebody mentioned recently the idea of an opCastFrom() (which is to opCast() as opBinaryRight() is to opBinary()) which may help here: if a struct/class declares opCastFrom(ubyte[]), then assigning [1,2,3] to the struct should cause the compiler to interpret the [1,2,3] as ubyte[] instead of int[]. T -- If Java had true garbage collection, most programs would delete themselves upon execution. -- Robert Sewell
May 16 2012
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 05/16/12 23:46, H. S. Teoh wrote:
 On Wed, May 16, 2012 at 11:34:18PM +0200, Timon Gehr wrote:
 On 05/16/2012 11:09 PM, H. S. Teoh wrote:
 OK, this isn't the same as your nothrow wrapper, but the principle is
 the same. The funcWrap template can basically call _any_ function
 that returns _anything_.

 D just acquired whole new levels of cool for me. :-)

There are still some restrictions to be sorted out though. For example, try it with funcWrap(&printShort,1);

Yes I noticed that compiler type inference didn't work in that case. This is one area I really hope will be improved soon. I kept running into this in the new AA implementation: assigning [1,2,3] to ubyte[] works, but passing [1,2,3] to a template automatically forces it into int[] even though the template body then tries to assign it to a ubyte[], causing an error.

http://d.puremagic.com/issues/show_bug.cgi?id=4953 I don't have dmd here, hence can't check, but it seems there's a chance it's already fixed. artur
May 16 2012