Thread overview | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
October 20, 2005 Exception handling - the basics | ||||
---|---|---|---|---|
| ||||
Hello, I come from a C background (no C++), and I haven't got any idea how D's exception handling works. The D reference document doesn't provide examples, and the tutorial on dsource only covers one side - how to catch exceptions - but not how to throw them. If there is a tutorial that I can use for this, I'd be glad to hear about it. Please consider the program below that uses C style error handling. How could I rewrite it to use D style exceptions? Thanks, Mathias import std.c.stdio; int foo(int bar) /* Let's assume that for some reason 10 < bar < 20 is necessary */ { if(bar<10) return -1; else if(bar>20) return 1; else return 0; } void main() { int result; result = foo(15); if(result == -1) printf("bar too small!\n"); else if(result == 1) printf("bar too large!\n"); else if(result == 0) printf("bar O.K.!\n"); } |
October 20, 2005 Re: Exception handling - the basics | ||||
---|---|---|---|---|
| ||||
Posted in reply to Mathias | Here are a few examples, ask any questions you want. import std.stdio; /* SIMPLE EXAMPLE */ /**/ void foo(int bar) { if (bar<10) throw new Exception("bar too small!"); if (bar>20) throw new Exception("bar too large!"); } void main() { try { foo(15); } catch(Exception e) { writefln(e); } } /**/ /* CUSTOM EXCEPTION EXAMPLE */ /** class TooSmallException : Exception { this() { super("bar too small!"); } } class TooLargeException : Exception { this() { super("bar too large!"); } } void foo(int bar) { if (bar<10) throw new TooSmallException(); if (bar>20) throw new TooLargeException(); } void main() { try { foo(15); } catch(TooSmallException e) { writefln("SMALL,",e); } catch(TooLargeException e) { writefln("LARGE,",e); } } /**/ /* ASSERT/CONTRACT EXAMPLE */ /** void foo(int bar) in { assert(bar>10 && bar<20); } body { } void main() { foo(15); } /**/ /* ORIGINAL */ /** int foo(int bar) //Let's assume that for some reason 10 < bar < 20 is necessary { if(bar<10) return -1; else if(bar>20) return 1; else return 0; } void main() { int result; result = foo(15); if(result == -1) printf("bar too small!\n"); else if(result == 1) printf("bar too large!\n"); else if(result == 0) printf("bar O.K.!\n"); } /**/ Regan |
October 21, 2005 Re: Exception handling - the basics | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | Thanks for the examples. For now, I'm only looking into the first (simple) example: # if (bar<10) throw new Exception("bar too small!"); Does this line from your example create a new Exception object? If no, what is it that is actually "thrown"? Or is a class object created below in the main program? That means, in this line: # catch(Exception e) It seems a bit weird to me that a class object is thrown before it's created. Finally, why is writefln necessary to print the line? I was under the - probably false - impression that printf and writef do essentially the same. However, trying to print the line using: # printf("%.*s\n", e); yields an Error: Access Violation. Not that I insist on using printf, I'm just curious. Thanks for your patience :-) Mathias In article <opsyyozkzg23k2f5@nrage.netwin.co.nz>, Regan Heath says... >Here are a few examples, ask any questions you want. > >import std.stdio; > >/* SIMPLE EXAMPLE */ >/**/ >void foo(int bar) >{ > if (bar<10) throw new Exception("bar too small!"); > if (bar>20) throw new Exception("bar too large!"); >} > >void main() >{ > try { > foo(15); > } catch(Exception e) { > writefln(e); > } >} >/**/ > >/* CUSTOM EXCEPTION EXAMPLE */ >/** >class TooSmallException : Exception >{ > this() { super("bar too small!"); } >} > >class TooLargeException : Exception >{ > this() { super("bar too large!"); } >} > >void foo(int bar) >{ > if (bar<10) throw new TooSmallException(); > if (bar>20) throw new TooLargeException(); >} > >void main() >{ > try { > foo(15); > } catch(TooSmallException e) { > writefln("SMALL,",e); > } catch(TooLargeException e) { > writefln("LARGE,",e); > } >} >/**/ > >/* ASSERT/CONTRACT EXAMPLE */ >/** >void foo(int bar) >in { > assert(bar>10 && bar<20); >} >body { >} > >void main() >{ > foo(15); >} >/**/ > >/* ORIGINAL */ >/** >int foo(int bar) //Let's assume that for some reason 10 < bar < 20 is >necessary >{ > if(bar<10) return -1; > else if(bar>20) return 1; > else return 0; >} > >void main() >{ > int result; > result = foo(15); > if(result == -1) printf("bar too small!\n"); > else if(result == 1) printf("bar too large!\n"); > else if(result == 0) printf("bar O.K.!\n"); >} >/**/ > >Regan |
October 21, 2005 Re: Exception handling - the basics | ||||
---|---|---|---|---|
| ||||
Posted in reply to Mathias | Mathias wrote: > Thanks for the examples. For now, I'm only looking into the first (simple) > example: > > # if (bar<10) throw new Exception("bar too small!"); > > Does this line from your example create a new Exception object? If no, what is it that is actually "thrown"? Or is a class object created below in the main program? Yes, it creates a new Exception object, which is then thrown. The rule of thumb is that if you're not sure what something does, read it out as an English sentence - "throw new exception": a new exception is thrown ;-) (Okay, it doesn't always work that well, but it's worth a try.) > Finally, why is writefln necessary to print the line? I was under the - probably false - impression that printf and writef do essentially the same. However, trying to print the line using: > > # printf("%.*s\n", e); > > yields an Error: Access Violation. Not that I insist on using printf, I'm just curious. printf and writef differ in a couple of ways. One of these is that writef implicitly calls the toString method of any object it is passed: writef(e) is the same as writef(e.toString()) is the same as writef("%s", e.toString()) is the same as printf("%.*s", e.toString()). A small example to illustrate this: import std.stdio; class X { char[] toString() { return "abc"; } } int main() { X x = new X; writefln(x); // "abc" writefln(x.toString); // "abc" writefln("%s", x); // "abc" writefln("%s", x.toString); // "abc" printf("%.*s\n", x.toString()); // "abc" printf("%.*s\n", x); // "%.*s" return 0; } That didn't go quite as I hoped, the last printf doesn't give an access violation error, and I'm not quite sure why :-) I hope I managed to explain the problem in your case, though. |
October 21, 2005 Re: Exception handling - the basics | ||||
---|---|---|---|---|
| ||||
Posted in reply to Deewiant | Yes that makes it clearer for me. However, still a question: # catch(Exception e) Does this create an Exception object as well? Does that mean that the program creates two Exception objects, one where the exception is thrown, and the other where it is catched? Also, would you agree that the docs are not complete with regards to writef et al? Or are the things you explained to me there, just that I didn't find them? If so, where are they? Thank you for your time, Mathias In article <djbdmf$2gqk$1@digitaldaemon.com>, Deewiant says... > >Mathias wrote: >> Thanks for the examples. For now, I'm only looking into the first (simple) >> example: >> >> # if (bar<10) throw new Exception("bar too small!"); >> >> Does this line from your example create a new Exception object? If no, what is it that is actually "thrown"? Or is a class object created below in the main program? > >Yes, it creates a new Exception object, which is then thrown. > >The rule of thumb is that if you're not sure what something does, read it out as an English sentence - "throw new exception": a new exception is thrown ;-) (Okay, it doesn't always work that well, but it's worth a try.) > >> Finally, why is writefln necessary to print the line? I was under the - probably false - impression that printf and writef do essentially the same. However, trying to print the line using: >> >> # printf("%.*s\n", e); >> >> yields an Error: Access Violation. Not that I insist on using printf, I'm just curious. > >printf and writef differ in a couple of ways. One of these is that writef >implicitly calls the toString method of any object it is passed: writef(e) is >the same as writef(e.toString()) is the same as writef("%s", e.toString()) is >the same as printf("%.*s", e.toString()). > >A small example to illustrate this: > >import std.stdio; > >class X { > char[] toString() { return "abc"; } >} > >int main() { > X x = new X; > > writefln(x); // "abc" > writefln(x.toString); // "abc" > writefln("%s", x); // "abc" > writefln("%s", x.toString); // "abc" > printf("%.*s\n", x.toString()); // "abc" > printf("%.*s\n", x); // "%.*s" > > return 0; >} > >That didn't go quite as I hoped, the last printf doesn't give an access violation error, and I'm not quite sure why :-) I hope I managed to explain the problem in your case, though. |
October 21, 2005 Re: Exception handling - the basics | ||||
---|---|---|---|---|
| ||||
Posted in reply to Mathias | In article <djbgmb$2jqb$1@digitaldaemon.com>, Mathias says... > >Yes that makes it clearer for me. However, still a question: > ># catch(Exception e) > >Does this create an Exception object as well? Does that mean that the program creates two Exception objects, one where the exception is thrown, and the other where it is catched? No. The only exception object that is created is when you call 'new'. From there, the pointer/handle is passed to the catch clause. You can even do something like this if you really want to: static Exception myExcept; static this { myExcept = new Exception; } void main() { try { throw myExcept; } catch( Exception e ) { printf( "%.*s\n", e.toString() ); } } ie. it's not strictly necessary to dynamically allocate an exception object as a part of the throw process. Sean |
October 22, 2005 Re: Exception handling - the basics | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | Ah, I think I'm beginning to understand... ;-) I was confused by the line: # catch(Exception e) and what it does. I didn't understand why I couldn't simply write: # catch(e) I think the correct answer is that the compiler simply doesn't know what "e" is since it is created in another function. The fact that the throwing function knows what kind of object it throws doesn't change this. "catch(e)" just tells the compiler "catch something called 'e'", and the compiler replies: "I can't do anything unless I know what kind of data 'e' is." "catch (Exception e)" tells the compiler "catch an Exception object that we call 'e' here" and the compiler says "O.K." If both throw and catch were located within the same function, I could indeed simply write catch(e). I hope this is correct now. (If not, please tell me so.) Thanks, Mathias |
October 22, 2005 Re: Exception handling - the basics | ||||
---|---|---|---|---|
| ||||
Posted in reply to Mathias | In article <djd3ab$138g$1@digitaldaemon.com>, Mathias says... > >Ah, I think I'm beginning to understand... ;-) I was confused by the line: > ># catch(Exception e) > >and what it does. I didn't understand why I couldn't simply write: > ># catch(e) > >I think the correct answer is that the compiler simply doesn't know what "e" is since it is created in another function. The fact that the throwing function knows what kind of object it throws doesn't change this. Right. It's a bit like an inline function call. You can also have a series of catch exceptions, and the one that is called will be the first one that matches: class MyException : Exception {} try { throw new MyException; } catch( MyException e ) {} // exact match, so enter this catch clause catch( Exception e ) {} try { throw new MyException; } catch( Exception e ) {} // MyException implicitly converts to Exception because it derives from this class--enter this catch clause catch( MyException e ) {} // even though this is a better match, it will not be called before catch(Exception) will be evaluated first Sean |
October 23, 2005 Re: Exception handling - the basics | ||||
---|---|---|---|---|
| ||||
Posted in reply to Mathias | In article <djd3ab$138g$1@digitaldaemon.com>, Mathias says... > >"catch(e)" just tells the compiler "catch something called 'e'", and the compiler replies: "I can't do anything unless I know what kind of data 'e' is." "catch (Exception e)" tells the compiler "catch an Exception object that we call 'e' here" and the compiler says "O.K." By the way, the compiler is not forced to process an exception in any specific scope--it will just keep unwinding the stack until it finds an appropriate catch handler. For example: class MyException {} // note, not derived from Exception void f1() { throw new MyException; } void f2() { try { f1(); } catch( Exception e ) {} // will be skipped because MyException does not derive from Exception } void f3() { try { f2(); } catch( Exception e ) {} // will be skipped as well catch( MyException e ) {} // exact match, process exception here catch( Object o ) {} // since all classes are objects, this is equivalent to "catch anything" } That said, I believe that all exceptions that are defined should inherit from Exception, and that you should only ever throw these objects. While the language may not prevent you from throwing other things (arrays, integers, etc), it's bad form to do so (I'm actually not sure if D lets you throw integers and such or not, but I thought I'd mention it just in case). >If both throw and catch were located within the same function, I could indeed >simply write catch(e). Not in D. As I said in my other post, catch clauses are kind of like inline functions--you must declare the type of exception they are meant to handle and a name for this variable. C++ allows "catch(...)" for "catch anything," but D has no direct equivalent--the closest you can get is "catch(Object o)." Sean |
October 23, 2005 Re: Exception handling - the basics | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | "Sean Kelly" <sean@f4.ca> wrote in message news:djer01$r2t$1@digitaldaemon.com... > In article <djd3ab$138g$1@digitaldaemon.com>, Mathias says... >> >>"catch(e)" just tells the compiler "catch something called 'e'", and the >>compiler replies: "I can't do anything unless I know what kind of data 'e' >>is." >>"catch (Exception e)" tells the compiler "catch an Exception object that >>we call >>'e' here" and the compiler says "O.K." > > By the way, the compiler is not forced to process an exception in any > specific > scope--it will just keep unwinding the stack until it finds an appropriate > catch > handler. For example: > > class MyException {} // note, not derived from Exception > > void f1() { > throw new MyException; > } > > void f2() { > try { f1(); } > catch( Exception e ) {} // will be skipped because MyException does not > derive > from Exception > } > > void f3() { > try { f2(); } > catch( Exception e ) {} // will be skipped as well > catch( MyException e ) {} // exact match, process exception here > catch( Object o ) {} // since all classes are objects, this is equivalent > to > "catch anything" > } > > That said, I believe that all exceptions that are defined should inherit > from > Exception, and that you should only ever throw these objects. While the > language may not prevent you from throwing other things (arrays, integers, > etc), > it's bad form to do so (I'm actually not sure if D lets you throw integers > and > such or not, but I thought I'd mention it just in case). > >>If both throw and catch were located within the same function, I could >>indeed >>simply write catch(e). > > Not in D. As I said in my other post, catch clauses are kind of like > inline > functions--you must declare the type of exception they are meant to handle > and a > name for this variable. C++ allows "catch(...)" for "catch anything," but > D has > no direct equivalent--the closest you can get is "catch(Object o)." Just to correct you, D does have an equivalent to C++'s catch (...), and it's 'catch' on it's own. try { throw new Exception("Exception thrown."); } catch { // catches anything - eg, when you're not interested in what was thrown } John. > > > Sean > > |
Copyright © 1999-2021 by the D Language Foundation