www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Contracts or Exceptions?

reply Mike Linford <mike.linford.reg gmail.com> writes:
Hello,

So I'm writing a function for a library. It takes a struct as an 
argument. The struct's fields can't just be any old values, though. The 
function won't work if some of the fields are weird. It could return an 
erroneous value, or even crash. 

The thing is, though, that I can't tell whether the best approach is to 
use an in-contract or throw an exception if it doesn't pass my test. What 
would you guys recommend?



-- 
--Mike Linford
Mar 29 2011
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 29 Mar 2011 14:40:02 -0400, Mike Linford  
<mike.linford.reg gmail.com> wrote:

 Hello,

 So I'm writing a function for a library. It takes a struct as an
 argument. The struct's fields can't just be any old values, though. The
 function won't work if some of the fields are weird. It could return an
 erroneous value, or even crash.

 The thing is, though, that I can't tell whether the best approach is to
 use an in-contract or throw an exception if it doesn't pass my test. What
 would you guys recommend?

This has been discussed very recently on the main newsgroup (digitalmars.D). It is not an easy question to answer. There are two ways to answer it easily: 1. if the input to the function will always be deterministic, meaning there is no chance some environmental cause can affect the input, or the validity of the input, then use contracts, this should catch any code errors during testing. 2. if the input to the function will always be generated external to the program (i.e. from a file), then you should use exceptions. If it could be either, there are different schools of thought on this. If the function isn't performance critical, throwing an exception is likely a good choice. However, if it is performance critical, I'd recommend using contracts. Then if you have a situation when your input is externally generated, you need to check it outside the function with an exception thrown on error. -Steve
Mar 29 2011
parent spir <denis.spir gmail.com> writes:
On 03/29/2011 08:49 PM, Steven Schveighoffer wrote:
 On Tue, 29 Mar 2011 14:40:02 -0400, Mike Linford <mike.linford.reg gmail.com>
 wrote:

 Hello,

 So I'm writing a function for a library. It takes a struct as an
 argument. The struct's fields can't just be any old values, though. The
 function won't work if some of the fields are weird. It could return an
 erroneous value, or even crash.

 The thing is, though, that I can't tell whether the best approach is to
 use an in-contract or throw an exception if it doesn't pass my test. What
 would you guys recommend?

This has been discussed very recently on the main newsgroup (digitalmars.D). It is not an easy question to answer. There are two ways to answer it easily: 1. if the input to the function will always be deterministic, meaning there is no chance some environmental cause can affect the input, or the validity of the input, then use contracts, this should catch any code errors during testing. 2. if the input to the function will always be generated external to the program (i.e. from a file), then you should use exceptions. If it could be either, there are different schools of thought on this. If the function isn't performance critical, throwing an exception is likely a good choice. However, if it is performance critical, I'd recommend using contracts. Then if you have a situation when your input is externally generated, you need to check it outside the function with an exception thrown on error.

... or users of your service should be warned it is their reponsability to check validity of inputs to it (case of a lib). Denis -- _________________ vita es estrany spir.wikidot.com
Mar 29 2011
prev sibling next sibling parent reply Kai Meyer <kai unixlords.com> writes:
On 03/29/2011 12:40 PM, Mike Linford wrote:
 Hello,

 So I'm writing a function for a library. It takes a struct as an
 argument. The struct's fields can't just be any old values, though. The
 function won't work if some of the fields are weird. It could return an
 erroneous value, or even crash.

 The thing is, though, that I can't tell whether the best approach is to
 use an in-contract or throw an exception if it doesn't pass my test. What
 would you guys recommend?

I was given two words of advice on exceptions: "Use exceptions for the exceptional" "Use exceptions only for the exceptional" Meaning, if you expect something to fail frequently, using a try/catch is much more expensive than an if/else. "The exceptional" is something that should rarely ever occur, like running out of memory. For contracts, they are usually meant as a developer tool to warn them when that they will be in no-man's land if they attempt to proceed any further. They are, therefore, not compiled in when you pass the D compiler the -release flag. I don't think that contracts and exceptions are mutually exclusive. They provide two different safeguards. One is a tool to aid in the development process, one is a tool to safeguard against exceptional run-time scenarios. However, I think if I rephrase your question a little bit, it might provide you what I think you're after. I'm making an assumption here, so I may be way off the mark. "Who's responsibility is it to check whether or not the data passed into the function is valid? Should I accept whatever the user wants to pass in, and let them know when it's invalid, or should I trust that the user is sending in good data?" I think that's a choice for you to make. Contract programming is often used to put the burden of validation on the user of the library (meaning programmer writing software taht uses your library). Exceptions could be used to indicate that the library writer is accepting responsibility for validating the data before using it. But validating data is not the only reason to use exceptions, and it's not unusual for a library to skip all validations and force the user to do all the checking before hand. Arrays are a good example. When not in -release mode, array boundaries are checked upon every access to the array, and an exception is thrown if access goes out of bounds. In -release mode, if you go out of bounds you get a segfault. This is one example of giving the user (programmer) of arrays the responsibility to check boundaries so that the library can focus on being fast. I think the choice comes down to a balance between performance, responsibility, and maintainability. (Oh, and you'll usually get it wrong the first time, so don't be sad.)
Mar 29 2011
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/29/2011 03:40 PM, Kai Meyer wrote:

 I was given two words of advice on exceptions:
 "Use exceptions for the exceptional"
 "Use exceptions only for the exceptional"

Those advices are given by wise people: they are wise only because they leave the definition as vague as "exceptional." :) And what do we do for the "not so exceptional"? Do we return error codes? So the function implementation will be complicated and the caller code will be complicated. Exceptions are a great tool to eliminate the need for error codes. Here is what I follow: - Functions have specific tasks to do; if those tasks cannot be accomplished, the function must throw. In some cases the function can continue, but that behavior must be documented. For example, if an HTML library function is responsible for making HTML headers, of which only the levels in the range of 1-6 are valid, that function may throw when the level is outside of the valid range, for in that case it cannot "make an HTML header"; or it can document that if the level is outside of the range, 1 or 6 will be used. - Catch exceptions only when there is a sensible thing to do at that level: log an error, skip that operation, go back to the user with an error code, take corrective action, etc. Disclaimer: That is what I follow in C++ code. I don't have experience with exception safety in D. I don't know issues that may be specific to D. Ali
Mar 29 2011
next sibling parent reply Kai Meyer <kai unixlords.com> writes:
On 03/29/2011 09:32 PM, Ali Çehreli wrote:
 On 03/29/2011 03:40 PM, Kai Meyer wrote:

  > I was given two words of advice on exceptions:
  > "Use exceptions for the exceptional"
  > "Use exceptions only for the exceptional"

 Those advices are given by wise people: they are wise only because they
 leave the definition as vague as "exceptional." :)

Ya, now that I'm thinking about it a little more, we were talking about what sort of things you can do to increase performance. This was actually pretty far down the list, after considering things like profiling, improving algorithms, using built-in types instead of classes, ect. Exceptions are a little more expensive than condition statements.
 And what do we do for the "not so exceptional"? Do we return error
 codes? So the function implementation will be complicated and the caller
 code will be complicated.

You're right. I would consider those complications only if the optimisation I'm after justifies them. For example, if our profiler indicates that the function is running slowly due to handling a lot of exceptions (which probably won't be the first thing the profiler finds is running slow), we could use conditional statements to speed things up a bit. if (good data) do work else report "can't do work" Would be faster than: try do work catch report "can't do work" I'll also add that the same person who gave me this advice also likes to say "Premature optimisation is the root of all evil."
 Exceptions are a great tool to eliminate the need for error codes.

 Here is what I follow:

 - Functions have specific tasks to do; if those tasks cannot be
 accomplished, the function must throw.

 In some cases the function can continue, but that behavior must be
 documented. For example, if an HTML library function is responsible for
 making HTML headers, of which only the levels in the range of 1-6 are
 valid, that function may throw when the level is outside of the valid
 range, for in that case it cannot "make an HTML header"; or it can
 document that if the level is outside of the range, 1 or 6 will be used.

 - Catch exceptions only when there is a sensible thing to do at that
 level: log an error, skip that operation, go back to the user with an
 error code, take corrective action, etc.

 Disclaimer: That is what I follow in C++ code. I don't have experience
 with exception safety in D. I don't know issues that may be specific to D.

 Ali

Thanks for your insight Ali :) I'm not sure D's exceptions are much different than C++'s. I think you're right on.
Mar 30 2011
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/30/2011 08:42 AM, Kai Meyer wrote:

 we were talking about what sort of things you can do to
 increase performance

Sorry to change the topic. :) I am fortunate that what I currently work on does not require more than being careful about not using the wrong algorithms and data structures.
 if (good data)
 do work
 else
 report "can't do work"

That is helpful as long as the data can be checked up front, but we can't always be sure: if (file_exists()) { use_file(); // <-- the file may not exist or if (server_is_up()) { talk_to_server(); // <-- the server may not be up
 Would be faster than:

 try
 do work
 catch
 report "can't do work"

Apparently that's what Denis' (spir) measurements show as well and that's very unfortunate. I would like to put that slowness under the "quality of implementation" category. Although, it's well known that exception handling has been slow (e.g. in C++) in general too. (Note: I heard that scope(failure) is lowered to a try-catch block by the compiler; so it should be slow too.) If exceptions are inherently slow, that must be because they provide more than what we compare them against. A good example is comparing old features of C to C++. Some people claim that calling virtual functions is slow due to jumping off the vtbl. Correct, but let's not forget that achieving the same in C would be slow too. Your examples do have such a feature difference: the code that uses the try-catch block will always execute the code in the catch clause. On the other hand, the code that checks the data before hand may not report "can't do work", as "do work" may get complicated in the future and may call a function that may throw directly or indirectly. And suddenly the function doesn't work anymore and the changes that we've made are detached from this function. Bad bug! :) We may argue that exceptions should not exist in D (or C++) but they are so helpful (hey, I know we all know these :)): - exceptions make it impossible (or very hard) to continue with bad program state - they allow functions to return objects by freeing the return value from always being an error code (return values are preferable to side effects) - they eliminate boiler plate error management lines (To complicate matters, when a function needs to do cleanup after an error, it may call other functions that may return more error codes. We must be careful not to change the value of the original error code variable.) - they result in less lines of code (bugs live in lines of code :)) I would like to show two functions written in C and C++. To my knowledge, they are well written and don't have any resource leaks: // a C function int bar_C(Resource ** in_out) { int err = 0; Resource * r0 = NULL; Resource * r1 = NULL; err = allocate_resource(&r0); if (err) goto finally; err = allocate_resource(&r1); if (err) goto finally; /* ... use r0 and r1 here ... */ if (err) goto finally; /* transfer ownership */ *in_out = r0; r0 = NULL; finally: deallocate_resource(&r1); deallocate_resource(&r0); return err; } // The equivalent C++ function Resource bar_CPP() { Resource r0(/* ... */); Resource r1(/* ... */); /* ... use r0 and r1 here ... */ /* transfer ownership */ return r0; } Of course the latter takes advantage of other features of C++, but it also shows that there is no explicit error management when the code relies on exceptions. bar_CPP() just does what it is supposed to do. Yes, there may be errors but bar_CPP() doesn't care. And the callers may or may not catch the errors, but bar_CPP() doesn't care.
 I'm not sure D's exceptions are much different than C++'s.

Yeah, it must be the same as what Digital Mars C++ compiler uses. (Except, D also has the 'finally' clause.) In summary: I hope I will never go back to pass-the-error-code style of coding. Ali
Mar 30 2011
prev sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-03-30 05:09, spir wrote:
 On 03/30/2011 05:32 AM, Ali =C3=87ehreli wrote:
 On 03/29/2011 03:40 PM, Kai Meyer wrote:
  I was given two words of advice on exceptions:
  "Use exceptions for the exceptional"
  "Use exceptions only for the exceptional"

Those advices are given by wise people: they are wise only because they leave the definition as vague as "exceptional." :) =20 And what do we do for the "not so exceptional"? Do we return error code=


 So the function implementation will be complicated and the caller code
 will be complicated.
=20
 Exceptions are a great tool to eliminate the need for error codes.
=20
 Here is what I follow:
=20
 - Functions have specific tasks to do; if those tasks cannot be
 accomplished, the function must throw.
=20
 In some cases the function can continue, but that behavior must be
 documented. For example, if an HTML library function is responsible for
 making HTML headers, of which only the levels in the range of 1-6 are
 valid, that function may throw when the level is outside of the valid
 range, for in that case it cannot "make an HTML header"; or it can
 document that if the level is outside of the range, 1 or 6 will be used.
=20
 - Catch exceptions only when there is a sensible thing to do at that
 level: log an error, skip that operation, go back to the user with an
 error code, take corrective action, etc.
=20
 Disclaimer: That is what I follow in C++ code. I don't have experience
 with exception safety in D. I don't know issues that may be specific to
 D.

These are sensible and well expressed guidelines, thank you. In other languages, I happened to use exceptions as a // channel for side-information (eg 'nomatch' for a matching func), but in D I realised how 'exceptionnally' (!) costly throwing & catching exceptions is, so that I do not do it anymore. No idea though whether this exceptional cost is perticular to D.

I'd have to measure it in C++, Java, and C#, but I'm pretty sure that D's i= s=20 at least a lot slower than Java. Java uses exceptions all over the place, a= nd=20 it works quite well overall IMHO, but I never got the impression that there= =20 was any kind of major overhead for exceptions (though obviously there's goi= ng=20 to be at least _some_ performance hit for derailing the thread of execution= =20 like that). In D however, it seems to be significant. As I've been reworkin= g=20 std.datetime's unit tests, I've found that I've had to be very careful abou= t=20 how often I use assertThrown, or there is a _major_ increase in how long th= e=20 unit tests take to execute. While changing some tests, I had some loops whi= ch=20 included assertThrown. It turns out that with assertThrown, they'd be 10+=20 seconds long, whereas without, they'd be a matter of milliseconds. So, the= =20 performance of exceptions in D is quite poor and while it probably doesn't= =20 matter all that much for normal code execution, it's definitely annoying fo= r=20 heavy unit testing which is validating that functions throw when they're=20 supposed to throw. As a test, on my machine, this program =3D=3D=3D import std.datetime; import std.exception; import std.stdio; void main() { auto date =3D Date(2011, 5, 7); { auto curr =3D Clock.currTime(); assertNotThown!DateTimeException(date.day =3D 29); writeln(curr - Clock.currTime()); } { auto curr =3D Clock.currTime(); assertThrown!DateTimeException(date.day =3D 32); writeln(curr - Clock.currTime()); } } =3D=3D=3D prints out this =2D1 =CE=BCs and -4 hnsecs =2D637 =CE=BCs and -7 hnsecs Naturally, the executation time does vary some, but it's consistently over = 400=20 times (and generally more like 450 times) more expensive to have the except= ion=20 be thrown and caught than it is to have it not be thrown. That's _really_=20 expensive. I'd be stunned if Java's or C#'s were that bad, but I'd have to = go=20 and test it. C++'s might be, but given how much Java and C# use exceptions,= it=20 would ludicrous if either of them had that kind of overhead for exceptions. =2D Jonathan M Davis
Mar 30 2011
next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/30/2011 12:40 PM, Jonathan M Davis wrote:
 On 2011-03-30 05:09, spir wrote:
 On 03/30/2011 05:32 AM, Ali Çehreli wrote:
 On 03/29/2011 03:40 PM, Kai Meyer wrote:
   I was given two words of advice on exceptions:
   "Use exceptions for the exceptional"
   "Use exceptions only for the exceptional"

Those advices are given by wise people: they are wise only because they leave the definition as vague as "exceptional." :) And what do we do for the "not so exceptional"? Do we return error codes? So the function implementation will be complicated and the caller code will be complicated. Exceptions are a great tool to eliminate the need for error codes. Here is what I follow: - Functions have specific tasks to do; if those tasks cannot be accomplished, the function must throw. In some cases the function can continue, but that behavior must be documented. For example, if an HTML library function is responsible for making HTML headers, of which only the levels in the range of 1-6 are valid, that function may throw when the level is outside of the valid range, for in that case it cannot "make an HTML header"; or it can document that if the level is outside of the range, 1 or 6 will be used. - Catch exceptions only when there is a sensible thing to do at that level: log an error, skip that operation, go back to the user with an error code, take corrective action, etc. Disclaimer: That is what I follow in C++ code. I don't have experience with exception safety in D. I don't know issues that may be specific to D.

These are sensible and well expressed guidelines, thank you. In other languages, I happened to use exceptions as a // channel for side-information (eg 'nomatch' for a matching func), but in D I realised how 'exceptionnally' (!) costly throwing& catching exceptions is, so that I do not do it anymore. No idea though whether this exceptional cost is perticular to D.

I'd have to measure it in C++, Java, and C#, but I'm pretty sure that D's is at least a lot slower than Java. Java uses exceptions all over the place, and it works quite well overall IMHO, but I never got the impression that there was any kind of major overhead for exceptions (though obviously there's going to be at least _some_ performance hit for derailing the thread of execution like that). In D however, it seems to be significant. As I've been reworking std.datetime's unit tests, I've found that I've had to be very careful about how often I use assertThrown, or there is a _major_ increase in how long the unit tests take to execute. While changing some tests, I had some loops which included assertThrown. It turns out that with assertThrown, they'd be 10+ seconds long, whereas without, they'd be a matter of milliseconds. So, the performance of exceptions in D is quite poor and while it probably doesn't matter all that much for normal code execution, it's definitely annoying for heavy unit testing which is validating that functions throw when they're supposed to throw. As a test, on my machine, this program === import std.datetime; import std.exception; import std.stdio; void main() { auto date = Date(2011, 5, 7); { auto curr = Clock.currTime(); assertNotThown!DateTimeException(date.day = 29); writeln(curr - Clock.currTime()); } { auto curr = Clock.currTime(); assertThrown!DateTimeException(date.day = 32); writeln(curr - Clock.currTime()); } } === prints out this -1 μs and -4 hnsecs -637 μs and -7 hnsecs

That's too much. :) I get consistent results with -O on a 64-bit Ubuntu: -1 μs and -9 hnsecs -832 μs and -9 hnsecs But with the addition of the -m64 flag, it's more than 4 times faster: -1 μs -175 μs and -4 hnsecs Still not good. :-/ Ali
 Naturally, the executation time does vary some, but it's consistently over 400
 times (and generally more like 450 times) more expensive to have the exception
 be thrown and caught than it is to have it not be thrown. That's _really_
 expensive. I'd be stunned if Java's or C#'s were that bad, but I'd have to go
 and test it. C++'s might be, but given how much Java and C# use exceptions, it
 would ludicrous if either of them had that kind of overhead for exceptions.

 - Jonathan M Davis

Mar 30 2011
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 Naturally, the executation time does vary some, but it's consistently over 400 
 times (and generally more like 450 times) more expensive to have the exception 
 be thrown and caught than it is to have it not be thrown.

On Windows in a benchmark I've seen thrown exceptions about as 12 times slower than Java ones. Bye, bearophile
Mar 30 2011
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On 03/30/2011 05:32 AM, Ali Çehreli wrote:
 On 03/29/2011 03:40 PM, Kai Meyer wrote:

  I was given two words of advice on exceptions:
  "Use exceptions for the exceptional"
  "Use exceptions only for the exceptional"

Those advices are given by wise people: they are wise only because they leave the definition as vague as "exceptional." :) And what do we do for the "not so exceptional"? Do we return error codes? So the function implementation will be complicated and the caller code will be complicated. Exceptions are a great tool to eliminate the need for error codes. Here is what I follow: - Functions have specific tasks to do; if those tasks cannot be accomplished, the function must throw. In some cases the function can continue, but that behavior must be documented. For example, if an HTML library function is responsible for making HTML headers, of which only the levels in the range of 1-6 are valid, that function may throw when the level is outside of the valid range, for in that case it cannot "make an HTML header"; or it can document that if the level is outside of the range, 1 or 6 will be used. - Catch exceptions only when there is a sensible thing to do at that level: log an error, skip that operation, go back to the user with an error code, take corrective action, etc. Disclaimer: That is what I follow in C++ code. I don't have experience with exception safety in D. I don't know issues that may be specific to D.

These are sensible and well expressed guidelines, thank you. In other languages, I happened to use exceptions as a // channel for side-information (eg 'nomatch' for a matching func), but in D I realised how 'exceptionnally' (!) costly throwing & catching exceptions is, so that I do not do it anymore. No idea though whether this exceptional cost is perticular to D. Denis -- _________________ vita es estrany spir.wikidot.com
Mar 30 2011
prev sibling parent reply Kagamin <spam here.lot> writes:
Kai Meyer Wrote:

 do all the checking before hand. Arrays are a good example. When not in 
 -release mode, array boundaries are checked upon every access to the 
 array, and an exception is thrown if access goes out of bounds. In 
 -release mode, if you go out of bounds you get a segfault.

No, you get a remote root vulnerability.
Mar 30 2011
parent reply Kai Meyer <kai unixlords.com> writes:
On 03/30/2011 08:25 AM, Kagamin wrote:
 Kai Meyer Wrote:

 do all the checking before hand. Arrays are a good example. When not in
 -release mode, array boundaries are checked upon every access to the
 array, and an exception is thrown if access goes out of bounds. In
 -release mode, if you go out of bounds you get a segfault.

No, you get a remote root vulnerability.

Sure, but the point is that arrays can be used in a way that performs as fast as possible, therefore it becomes the programmer's job to ensure that all access to the array is within bounds, which is faster than making the array check itself every time.
Mar 30 2011
parent Kagamin <spam here.lot> writes:
Kai Meyer Wrote:

 On 03/30/2011 08:25 AM, Kagamin wrote:
 Kai Meyer Wrote:

 do all the checking before hand. Arrays are a good example. When not in
 -release mode, array boundaries are checked upon every access to the
 array, and an exception is thrown if access goes out of bounds. In
 -release mode, if you go out of bounds you get a segfault.

No, you get a remote root vulnerability.

Sure, but the point is that arrays can be used in a way that performs as fast as possible, therefore it becomes the programmer's job to ensure that all access to the array is within bounds, which is faster than making the array check itself every time.

It is faster, but I'm not sure it's so much faster to be worth doing. Really, you shouldn't ship unsafe version by default. It should be the user's right, decision and responsibility to do it.
Mar 31 2011
prev sibling next sibling parent Mike Linford <mike.linford.reg gmail.com> writes:
Thanks for the responses, everybody. They were helpful :-)

-- 
--Mike Linford
Mar 29 2011
prev sibling next sibling parent Kagamin <spam here.lot> writes:
Mike Linford Wrote:

 Hello,
 
 So I'm writing a function for a library. It takes a struct as an 
 argument. The struct's fields can't just be any old values, though. The 
 function won't work if some of the fields are weird. It could return an 
 erroneous value, or even crash. 
 
 The thing is, though, that I can't tell whether the best approach is to 
 use an in-contract or throw an exception if it doesn't pass my test. What 
 would you guys recommend?

Error (like AssertError) is intended to be irrecoverable like out of memory or stack overflow, Exception is intended to be catchable.
Mar 30 2011
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-03-30 14:05, Ali =C3=87ehreli wrote:
 On 03/30/2011 12:40 PM, Jonathan M Davis wrote:
 On 2011-03-30 05:09, spir wrote:
 On 03/30/2011 05:32 AM, Ali =C3=87ehreli wrote:
 On 03/29/2011 03:40 PM, Kai Meyer wrote:
   I was given two words of advice on exceptions:
   "Use exceptions for the exceptional"
   "Use exceptions only for the exceptional"

Those advices are given by wise people: they are wise only because th=




 leave the definition as vague as "exceptional." :)
=20
 And what do we do for the "not so exceptional"? Do we return error
 codes? So the function implementation will be complicated and the
 caller code will be complicated.
=20
 Exceptions are a great tool to eliminate the need for error codes.
=20
 Here is what I follow:
=20
 - Functions have specific tasks to do; if those tasks cannot be
 accomplished, the function must throw.
=20
 In some cases the function can continue, but that behavior must be
 documented. For example, if an HTML library function is responsible f=




 making HTML headers, of which only the levels in the range of 1-6 are
 valid, that function may throw when the level is outside of the valid
 range, for in that case it cannot "make an HTML header"; or it can
 document that if the level is outside of the range, 1 or 6 will be
 used.
=20
 - Catch exceptions only when there is a sensible thing to do at that
 level: log an error, skip that operation, go back to the user with an
 error code, take corrective action, etc.
=20
 Disclaimer: That is what I follow in C++ code. I don't have experience
 with exception safety in D. I don't know issues that may be specific =




 D.

These are sensible and well expressed guidelines, thank you. In other languages, I happened to use exceptions as a // channel for side-information (eg 'nomatch' for a matching func), but in D I realis=



 how 'exceptionnally' (!) costly throwing&  catching exceptions is, so
 that I do not do it anymore. No idea though whether this exceptional
 cost is perticular to D.

I'd have to measure it in C++, Java, and C#, but I'm pretty sure that D=


 is at least a lot slower than Java. Java uses exceptions all over the
 place, and it works quite well overall IMHO, but I never got the
 impression that there was any kind of major overhead for exceptions
 (though obviously there's going to be at least _some_ performance hit
 for derailing the thread of execution like that). In D however, it seems
 to be significant. As I've been reworking std.datetime's unit tests,
 I've found that I've had to be very careful about how often I use
 assertThrown, or there is a _major_ increase in how long the unit tests
 take to execute. While changing some tests, I had some loops which
 included assertThrown. It turns out that with assertThrown, they'd be
 10+ seconds long, whereas without, they'd be a matter of milliseconds.
 So, the performance of exceptions in D is quite poor and while it
 probably doesn't matter all that much for normal code execution, it's
 definitely annoying for heavy unit testing which is validating that
 functions throw when they're supposed to throw.
=20
 As a test, on my machine, this program
=20
 =3D=3D=3D
 import std.datetime;
 import std.exception;
 import std.stdio;
=20
 void main()
 {
=20
      auto date =3D Date(2011, 5, 7);
      {
     =20
          auto curr =3D Clock.currTime();
          assertNotThown!DateTimeException(date.day =3D 29);
          writeln(curr - Clock.currTime());
     =20
      }
     =20
      {
     =20
          auto curr =3D Clock.currTime();
          assertThrown!DateTimeException(date.day =3D 32);
          writeln(curr - Clock.currTime());
     =20
      }
=20
 }
 =3D=3D=3D
=20
 prints out this
=20
 -1 =CE=BCs and -4 hnsecs
 -637 =CE=BCs and -7 hnsecs

That's too much. :) I get consistent results with -O on a 64-bit Ubuntu: =20 -1 =CE=BCs and -9 hnsecs -832 =CE=BCs and -9 hnsecs =20 But with the addition of the -m64 flag, it's more than 4 times faster: =20 -1 =CE=BCs -175 =CE=BCs and -4 hnsecs =20 Still not good. :-/

LOL. And I just realized that I did those subtractions backwards. They're=20 negative where they should be positive. Oh well. The numbers are still clea= r,=20 and I was in a hurry when I wrote the test code. =2D Jonathan M Davis
Mar 30 2011