www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Exception handling - the basics

reply Mathias <Mathias_member pathlink.com> writes:
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");
}
Oct 20 2005
parent reply "Regan Heath" <regan netwin.co.nz> writes:
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
Oct 20 2005
parent reply Mathias <Mathias_member pathlink.com> writes:
Thanks for the examples. For now, I'm only looking into the first (simple)
example:



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:



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:



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
Oct 21 2005
parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
Mathias wrote:
 Thanks for the examples. For now, I'm only looking into the first (simple)
 example:
 

 
 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:
 

 
 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.
Oct 21 2005
parent reply Mathias <Mathias_member pathlink.com> writes:
Yes that makes it clearer for me. However, still a question:



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:
 

 
 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:
 

 
 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.
Oct 21 2005
parent reply Sean Kelly <sean f4.ca> writes:
In article <djbgmb$2jqb$1 digitaldaemon.com>, Mathias says...
Yes that makes it clearer for me. However, still a question:



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
Oct 21 2005
parent reply Mathias <Mathias_member pathlink.com> writes:
Ah, I think I'm beginning to understand... ;-) I was confused by the line:



and what it does. I didn't understand why I couldn't simply write:



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
Oct 22 2005
next sibling parent Sean Kelly <sean f4.ca> writes:
In article <djd3ab$138g$1 digitaldaemon.com>, Mathias says...
Ah, I think I'm beginning to understand... ;-) I was confused by the line:



and what it does. I didn't understand why I couldn't simply write:



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
Oct 22 2005
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
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
Oct 22 2005
parent reply "John C" <johnch_atms hotmail.com> writes:
"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

 
Oct 23 2005
parent Mathias <Mathias_member pathlink.com> writes:
Thanks heaps for your answers, Sean and John. I'm not sure I understand
everything that you say, but my main questions are answered. I guess I
will re-read your posts, test it on my computer, and someday I'll get
the remainder. ;-)

Thanks again,
Mathias
Oct 23 2005
prev sibling parent reply =?utf-8?B?RGF3aWQgQ2nEmcW8YXJraWV3aWN6?= <araelx gmail.com> writes:
On Sat, 22 Oct 2005 12:12:27 +0200, Mathias <Mathias_member pathlink.com>  
wrote:

 Ah, I think I'm beginning to understand... ;-) I was confused by the  
 line:



 and what it does. I didn't understand why I couldn't simply write:



 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.)
Yes, but no. <g> I see that you "hear the bell but don't know in which church does it ring". :) 1. Languages with "strong typing" (i'm not sure is this right term) have to know of which type is every used variable before they'll use it. They just have to reserve fixed amount of bytes first. And they have to know what you can do with such variable, and what you can not. This is one and you seem to get it. 2. But there is another reason too: You can have many "catch" clauses, and you have to tell the compiler which type of throwed object you want to handle. That it why even in PHP (where variables may be one time strings and another something else) you have to specify what do you want to handle. In folowing example you can do "something completly diffrent" if assercion failed and "something completly diffrent" if other type of exception was throwed. try { ... } catch (AssertException a) { writef("Assert"); } catch (Exception e) { writef("other exception"); } That is why even if throw and catch would be in same function you have to add type first. They could be just many throws in such function and each of them could throw diffrent things. I do not wish you to turn you off using this group (as you can see many helpful people are here), but I think you should propably find some good book/web resource to learn faster about standard programing concepts like exceptions. D is very similar to C++ in many concepts so don't be afraid to use C++ examples (there is much more of them in the moment). What you have to keep in mind (dealing with exceptions in D, knowing how they work in C++) is that Objects in D are allways passed by reference (no copying is ever made, only handlers are passed) and of course D has neat "finally" keyword. Regards, -- Dawid Ciężarkiewicz
Oct 23 2005
parent Mathias <Mathias_member pathlink.com> writes:
In article <op.sy4c2qee58xlqs localhost.localdomain>,
=?utf-8?B?RGF3aWQgQ2nEmcW8YXJraWV3aWN6?= says...

I do not wish you to turn you off using this group (as you can see many  
helpful people are here), but I think you should propably find some good  
book/web resource to learn faster about standard programing concepts like  
exceptions. D is very similar to C++ in many concepts so don't be afraid  
to use C++ examples (there is much more of them in the moment). What you  
have to keep in mind (dealing with exceptions in D, knowing how they work  
in C++) is that Objects in D are allways passed by reference (no copying  
is ever made, only handlers are passed) and of course D has neat "finally"  
keyword.
Coming from C, I once tried to learn C++, which I found rather confusing. D seemed like an easier alternative, but in a sense it requires C++ knowledge anyway. A standalone, no prior knowledge requiring D tutorial would be useful. (More detailed, comprehensive and up-to-date than the one on dource.org.) It doesn't seem that there is something in hand, however. Anyway, thanks for your hints, they made things clearer for me. I'll also look into some C++ books. You are probably right in that this is a faster way to finally "get" it. Thanks again, Mathias
Oct 24 2005