www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Custom default exception handler?

reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
I don't suppose there's a way to change the default exception handler 
without using a modified druntime? I'm not seeing one in the docs, but I 
could have overlooked something.

Replacing a druntime function at link-time wouldn't be ideal because 
then druntime's handler couldn't be called as a fallback, at least 
without maintaining a duplicate of druntime's handler and keeping it in 
sync with the version of druntime being used.
Feb 10 2014
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Tuesday, 11 February 2014 at 03:53:05 UTC, Nick Sabalausky 
wrote:
 I don't suppose there's a way to change the default exception 
 handler without using a modified druntime
I'm pretty sure there used to be, but not anymore looking at the source. The d_run_main function has a hardcoded catch block. Why do you need to change the default though? Can't you just wrap your own main function in a big try/catch block?
Feb 10 2014
parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 2/10/2014 10:57 PM, Adam D. Ruppe wrote:
 On Tuesday, 11 February 2014 at 03:53:05 UTC, Nick Sabalausky wrote:
 I don't suppose there's a way to change the default exception handler
 without using a modified druntime
I'm pretty sure there used to be, but not anymore looking at the source. The d_run_main function has a hardcoded catch block. Why do you need to change the default though? Can't you just wrap your own main function in a big try/catch block?
I don't strictly *need* to. But if you're curious, here's the story: I like to use a little custom exception (Fail) in shell script-like stuff to bail out and exit with a given error message in an exception-safe way. This is for expected failure conditions, not internal errors (so for example: "copy src.txt" -> "Error, no destination given!" or "File src.txt doesn't exist!", etc), so the stack trace is unnecessary noise and omitted. Only the message is printed, maybe with a common prefix like "mytool: ERROR: ...". This is arguably a slight abuse of the exception system, but in script-like stuff the exception performance doesn't really matter, and I find it does greatly simply the error logic of D-based scripts. Helps keep simple scripts simple. I'm sticking this Fail stuff into a little utility lib for simple script-like programs, and so, if possible, I'd *like* to instruct users to just do this: void main() { installFailHandler(); ... } instead of all this boilerplate: int main() { try { ...user code... } catch(Fail e) { writeln(e.msg); return 1; } return 0; } I'm sure I could also do something like this, but it's rather ugly: int main() { mixin(handleFail!(() => { ...user code... })); } So not a real big deal, but it'd be nice if I could swing it.
Feb 10 2014
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Tuesday, 11 February 2014 at 05:09:13 UTC, Nick Sabalausky 
wrote:
     mixin(handleFail!(() => {
         ...user code...
     }));
BTW the ()=> there is unnecessary; when there's no arguments, you can just write { code } and it will be recognized as a function/delegate. So this would work too: int handleError(void delegate() dg) { try dg(); catch(Throwable t) return 1; return 0; } int main() { return handleError({ }); } (or of course, handleError(alias dg)() works too if you add the !). Still perhaps a bit wordier than installing the handler but I don't think it is too bad. You could also do something like i do in my cgi.d: void mymain() { code ... } mixin HandleError!mymain; // HandleError is a mixin tempalte that provides main(), does arg/exception handling, and returns the right value
Feb 11 2014
next sibling parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 2/11/2014 10:00 AM, Adam D. Ruppe wrote:
 So this would work too:

 int handleError(void delegate() dg) {
          try dg();
          catch(Throwable t) return 1;
          return 0;
 }

 int main() {
          return handleError({

          });
 }
Oh yea, good point. That's not bad at all;
Feb 11 2014
prev sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Adam D. Ruppe:

 int handleError(void delegate() dg) {
         try dg();
         catch(Throwable t) return 1;
Is it possible to use a lazy argument there? And isn't it better to catch Exception only? Bye, bearophile
Feb 11 2014
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 12 February 2014 at 01:08:42 UTC, bearophile wrote:
 Is it possible to use a lazy argument there?
I think it depends on the task. To just wrap generic code though a delegate seems most straightforward.
 And isn't it better to catch Exception only?
Perhaps, or the specific FailException type or whatever.
Feb 11 2014
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2014-02-11 06:09, Nick Sabalausky wrote:

 I don't strictly *need* to. But if you're curious, here's the story:

 I like to use a little custom exception (Fail) in shell script-like
 stuff to bail out and exit with a given error message in an
 exception-safe way. This is for expected failure conditions, not
 internal errors (so for example: "copy src.txt" -> "Error, no
 destination given!" or "File src.txt doesn't exist!", etc), so the stack
 trace is unnecessary noise and omitted. Only the message is printed,
 maybe with a common prefix like "mytool: ERROR: ...".
Perhaps you can do something with core.runtime.Runtime.traceHandler. -- /Jacob Carlborg
Feb 11 2014
parent reply "Sean Kelly" <sean invisibleduck.org> writes:
Throw a static exception (maybe even derived directly from 
Throwable), similar to OutOfMemory, with a custom toString.  That 
should eliminate the formatting you don't like and will prevent 
the trace from occurring as well (see rt/deh.d in Druntime--the 
trace isn't run if you throw typeid(t).init.ptr).
Feb 11 2014
next sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 2/11/2014 6:35 PM, Sean Kelly wrote:
 Throw a static exception (maybe even derived directly from Throwable),
 similar to OutOfMemory, with a custom toString.  That should eliminate
 the formatting you don't like and will prevent the trace from occurring
 as well (see rt/deh.d in Druntime--the trace isn't run if you throw
 typeid(t).init.ptr).
Oh, interesting. Is this something that can be relied on long-term? Ie, is a static non-Exception Throwable deliberately *supposed* to not include a stack trace, or is it potentially more of a currently-missing feature?
Feb 11 2014
parent "Sean Kelly" <sean invisibleduck.org> writes:
On Wednesday, 12 February 2014 at 01:07:31 UTC, Nick Sabalausky 
wrote:
 Oh, interesting. Is this something that can be relied on 
 long-term? Ie, is a static non-Exception Throwable deliberately 
 *supposed* to not include a stack trace, or is it potentially 
 more of a currently-missing feature?
It's intentional, and was done to serve two purposes. The first was to provide some way for throwing OutOfMemory to not accidentally try to allocate, and the second was because if you throw the same static instance in two threads simultaneously, the trace would end up invalid for one of them. The only safe thing to do is not trace at all.
Feb 11 2014
prev sibling next sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 2/11/2014 6:35 PM, Sean Kelly wrote:
 Throw a static exception (maybe even derived directly from Throwable),
I assume then that throwing something directly derived from Throwable would still run cleanup code (like scope guards and finally) like throwing Exception would? Or is it like throwing an Error, skipping cleanup code?
Feb 11 2014
parent "Sean Kelly" <sean invisibleduck.org> writes:
On Wednesday, 12 February 2014 at 02:41:34 UTC, Nick Sabalausky
wrote:
 On 2/11/2014 6:35 PM, Sean Kelly wrote:
 Throw a static exception (maybe even derived directly from 
 Throwable),
I assume then that throwing something directly derived from Throwable would still run cleanup code (like scope guards and finally) like throwing Exception would? Or is it like throwing an Error, skipping cleanup code?
Everything runs cleanup code right now, unless someone changed things on me. But if a change were made, I'd say that only Error and its children would skip cleanup. The default would be to perform cleanup. That lets users create their own exception hierarchies.
Feb 12 2014
prev sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 2/11/2014 6:35 PM, Sean Kelly wrote:
 Throw a static exception (maybe even derived directly from Throwable),
 similar to OutOfMemory, with a custom toString.  That should eliminate
 the formatting you don't like and will prevent the trace from occurring
 as well (see rt/deh.d in Druntime--the trace isn't run if you throw
 typeid(t).init.ptr).
Hmm, my custom toString isn't being executed. Am I doing something wrong here? Same result if I inherit direct from Throwable instead of Exception. class Fail : Exception { private this() { super(null); } private static Fail opCall(string msg, string file=__FILE__, int line=__LINE__) { auto f = cast(Fail) cast(void*) Fail.classinfo.init; f.msg = msg; f.file = file; f.line = line; return f; } override string toString() { writeln("In Fail.toString()"); return "someapp: ERROR: "~msg; } } void fail(string msg, string file=__FILE__, int line=__LINE__) { throw Fail(msg, file, line); } void main() { fail("Shit/fan collision detected."); } Output: scriptlike.Fail scriptlike.d(106): Shit/fan collision detected.
Feb 11 2014
parent reply "Sean Kelly" <sean invisibleduck.org> writes:
On Wednesday, 12 February 2014 at 03:31:38 UTC, Nick Sabalausky
wrote:
 Hmm, my custom toString isn't being executed. Am I doing 
 something wrong here? Same result if I inherit direct from 
 Throwable instead of Exception.

 class Fail : Exception
 {
     private this()
     {
         super(null);
     }

     private static Fail opCall(string msg, string 
 file=__FILE__, int line=__LINE__)
     {
         auto f = cast(Fail) cast(void*) Fail.classinfo.init;

         f.msg  = msg;
         f.file = file;
         f.line = line;

         return f;
     }

     override string toString()
     {
     writeln("In Fail.toString()");
         return "someapp: ERROR: "~msg;
     }

 }
It looks like this has changed, and the method that's called now is: void toString(scope void delegate(in char[]) sink) const; I suspect this has broken a lot of custom exception messages, since everything in core.exception still uses toString() for its output.
Feb 12 2014
parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 2/12/2014 4:14 PM, Sean Kelly wrote:
 It looks like this has changed, and the method that's called now
 is:

 void toString(scope void delegate(in char[]) sink) const;

 I suspect this has broken a lot of custom exception messages,
 since everything in core.exception still uses toString() for its
 output.
Hmm, that still isn't getting called for me either: void toString(scope void delegate(in char[]) sink) const { import std.stdio; writeln("In Fail.toString()"); sink("someapp: ERROR: "~msg); } Tried on both 2.064.2 and 2.065-b3. Could it be that the custom toString just doesn't get run for static exceptions?
Feb 12 2014
parent reply "Sean Kelly" <sean invisibleduck.org> writes:
On Wednesday, 12 February 2014 at 22:42:45 UTC, Nick Sabalausky
wrote:
 Hmm, that still isn't getting called for me either:

 void toString(scope void delegate(in char[]) sink) const
 {
 import std.stdio;
 writeln("In Fail.toString()");
     sink("someapp: ERROR: "~msg);
 }

 Tried on both 2.064.2 and 2.065-b3.

 Could it be that the custom toString just doesn't get run for 
 static exceptions?
It should. I'm not entirely sure why it isn't working. For reference, the relevant code is in rt/dmain2.d (printThrowable) and object_.d (toString(sink)) in Druntime.
Feb 12 2014
parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 2/12/2014 6:14 PM, Sean Kelly wrote:
 On Wednesday, 12 February 2014 at 22:42:45 UTC, Nick Sabalausky
 wrote:
 Tried on both 2.064.2 and 2.065-b3.

 Could it be that the custom toString just doesn't get run for static
 exceptions?
It should. I'm not entirely sure why it isn't working. For reference, the relevant code is in rt/dmain2.d (printThrowable) and object_.d (toString(sink)) in Druntime.
Argh, there seems to be something wonky with the new RDMD in 2.065-b3 (or hopefully it's just my system): If I use the RDMD from 2.064.2 to invoke DMD 2.065-b3 then this all works fine (thanks!). But if I use the RDMD/DMD *both* from DMD 2.065-b3 then...I dunno, either the exe doesn't get actually get rebuilt even with --force, or maybe it does but then runs an older copy of the exe, or something screwy like that. I need to look into that more. Looks like it's *not* going to work with DMD 2.064.2 though. In that version, printThrowable never actually calls Throwable.toString (*any* version of toString for that matter), and the sink version of Throwable.toString doesn't even exist. That's fine though, at least I know it'll work staring with 2.065.
Feb 12 2014
parent Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 2/13/2014 2:55 AM, Nick Sabalausky wrote:
 But if I use the RDMD/DMD *both* from DMD 2.065-b3
 then...I dunno, either the exe doesn't get actually get rebuilt even
 with --force, or maybe it does but then runs an older copy of the exe,
 or something screwy like that. I need to look into that more.
RDMD Regression Filed: https://d.puremagic.com/issues/show_bug.cgi?id=12149
Feb 13 2014