February 21, 2012 Re: The Right Approach to Exceptions | |
|---|---|
Posted in reply to Juan Manuel Cabo | Never mind modifying fields of the exception at some intermediate catch place.
Someone could even catch the exception and not rethrow it.
So: do some trusting. Life gets easier :-)
--jm
On 02/21/2012 12:46 PM, Juan Manuel Cabo wrote:
>> I think he meant to say things have been like that for a while and there's no blood in the streets.
>
> That's exactly what I meant.
>
|
February 21, 2012 Re: The Right Approach to Exceptions | |
|---|---|
Posted in reply to Juan Manuel Cabo | On Tuesday, 21 February 2012 at 15:38:15 UTC, Juan Manuel Cabo
wrote:
>> This works:
>> // note: the int parameter above isn't static
>> dbConn.query("select age from people where id='foobar'");
>> throw new WithErrorCode!FileNotFoundException(
>> db.rs.getValue(1), "file not found");
> ...
>> Can you offer a real world use-case where the above isn't
>> sufficient?
>
>
> What happened is that a file wasn't found. What one wants to
> catch is
> a FileNotFoundException.
>
> Do you suggest that I have to:
>
> try {
> ...
> } catch (FileNotFoundException ex) {
> ...
> } catch (WithErrorCode!FileNotFoundException ex) {
> ...
> } catch (WithRainbows!FileNotFoundException ex) {
> ...
> }
> and so on?
>
> --jm
FileNotFoundException is the super class of the others so the
first catch clause is enough. in fact, the others will never be
called if listed in the above order.
|
February 21, 2012 Re: The Right Approach to Exceptions | |
|---|---|
Posted in reply to foobar | > FileNotFoundException is the super class of the others so the first catch clause is enough. in fact, the others will
> never be called if listed in the above order.
Nice! I missed that. But what if you want to add ErrorCode and Rainbows?
And with your approach, one has to test for type and downcast, or
otherwise have multiple catch blocks (I don't want to miss plain
FileNotFoundExceptions). So it's square one.
With Variant[string] (or something equivalent, nothing better comes to mind)
one does:
try {
...
} catch (FileNotFoundException ex) {
if (ex.hasInfo(MyNameConstant)) {
... use that ...
}
... common handling ...
}
--jm
|
February 21, 2012 Re: The Right Approach to Exceptions | |
|---|---|
Posted in reply to Juan Manuel Cabo | Also, you would lose the stacktrace by rethrowing with a different exception object.
(Currently, the stacktrace is lost by rethrowing the same object, but the Exception.file
and Exception.line are not lost, and it seems that it is very easy to not lose the
stacktrace when rethrowing, and it is the correct thing (for instance, java doesn't
lose the stacktrace when rethrowing, and C++ with its throw; statement for rethrowing
doesn't either).
--jm
On 02/21/2012 01:15 PM, Juan Manuel Cabo wrote:
>> FileNotFoundException is the super class of the others so the first catch clause is enough. in fact, the others will
>> never be called if listed in the above order.
>
> Nice! I missed that. But what if you want to add ErrorCode and Rainbows?
> And with your approach, one has to test for type and downcast, or
> otherwise have multiple catch blocks (I don't want to miss plain
> FileNotFoundExceptions). So it's square one.
>
> With Variant[string] (or something equivalent, nothing better comes to mind)
> one does:
>
>
> try {
> ...
> } catch (FileNotFoundException ex) {
> if (ex.hasInfo(MyNameConstant)) {
> ... use that ...
> }
> ... common handling ...
> }
>
>
> --jm
>
>
|
February 21, 2012 Re: The Right Approach to Exceptions | |
|---|---|
Posted in reply to Andrei Alexandrescu | On Tuesday, 21 February 2012 at 14:56:52 UTC, Andrei Alexandrescu
wrote:
>>
>> Can you offer a real world use-case where the above isn't
>> sufficient?
>
> This has been discussed. A function would want to add
> contextual information to an exception and rethrow it.
> Requiring a new type for each such flow does not scale.
>
>
> Andrei
This solution works given the fact that I know what type of data
I want to add and I can't see a situation where that isn't the
case. In Juan's case, he wanted to add an error code so he _knew_
already what type is required (int).
It seems we did a full circle. I'll ask again, what are you
trying to optimize here? Number of instantiations?
|
February 21, 2012 Re: The Right Approach to Exceptions | |
|---|---|
Posted in reply to Juan Manuel Cabo | On Tuesday, 21 February 2012 at 16:15:17 UTC, Juan Manuel Cabo
wrote:
>> FileNotFoundException is the super class of the others so the
>> first catch clause is enough. in fact, the others will
>> never be called if listed in the above order.
>
> Nice! I missed that. But what if you want to add ErrorCode and
> Rainbows?
> And with your approach, one has to test for type and downcast,
> or
> otherwise have multiple catch blocks (I don't want to miss plain
> FileNotFoundExceptions). So it's square one.
>
> With Variant[string] (or something equivalent, nothing better
> comes to mind)
> one does:
>
>
> try {
> ...
> } catch (FileNotFoundException ex) {
> if (ex.hasInfo(MyNameConstant)) {
> ... use that ...
> }
> ... common handling ...
> }
>
>
> --jm
Regarding the downcast - you still perform a check in the code
above! You gained nothing by replacing a type check with a check
on a hash.
Regarding composition of several traits - even that simple
snippet is enough:
throw new
WithRainbows!withErrorCode!withFoobar!FileNotFoundException(...);
That's without further design which could probably improve this
further.
|
February 21, 2012 Re: The Right Approach to Exceptions | |
|---|---|
Posted in reply to foobar | On 2/21/12 10:39 AM, foobar wrote: > Regarding the downcast - you still perform a check in the code above! > You gained nothing by replacing a type check with a check on a > hash. You do gain because capability checks don't force a tree structure, whereas downcasting does. > Regarding composition of several traits - even that simple snippet is > enough: throw new > WithRainbows!withErrorCode!withFoobar!FileNotFoundException(...); > > That's without further design which could probably improve this > further. To quote a classic: > It's clear that you are trying to generify exceptions. This > contradicts the very notion of what exceptions are. You also seem to > try to optimize the amount of exception classes. Making user code > convoluted for the sake of some premature optimization which most > likely has negligible affect is completely unacceptable. I get that > you are a templates master, that does *NOT* mean everything must be > made generic. You seem to prove the old saying that when all you have > is a hammer everything looks like a nail. It's gotta be one or the other. Andrei |
February 21, 2012 Re: The Right Approach to Exceptions | |
|---|---|
Posted in reply to foobar | > throw new WithRainbows!withErrorCode!withFoobar!FileNotFoundException(...); So: catch (WithRainbows!withErrorCode!withFoobar!FileNotFoundException ex) { .... } catch (WithRainbows!withErrorCode!withFoobar!FileNotFoundException ex) { .... } catch (WithErrorCode!withRainbows!withFoobar!FileNotFoundException ex) { .... } catch (WithRainbows!withFoobar!withErrorCode!FileNotFoundException ex) { and so on (in this case will be, its 3! == 6). and you would have to write them all. You cannot catch only WithRainbows!* because you miss the FileNotFoundException at the end. Please, refer to my previous posts. I don't want to start to repaste my posts. In one of them, I said that what you care about for the catch selection is the *what* of the error. Not the *cause* of the error, not the *where* of the error (no one catches by *where*). And that it seems wrong to encode anything other than the *what* of the error in the type name. Other things such as the cause or the date should be encoded inside the exception object instead of in the exception class type name. I thought that an alternative to Variant[string] would be to have some virtual functions overrideable (getExceptionData(string dataName) or something). but they would all have to return Object or Variant, so it's the same thing. --jm On 02/21/2012 01:39 PM, foobar wrote: > On Tuesday, 21 February 2012 at 16:15:17 UTC, Juan Manuel Cabo wrote: >>> FileNotFoundException is the super class of the others so the first catch clause is enough. in fact, the others will >>> never be called if listed in the above order. >> >> Nice! I missed that. But what if you want to add ErrorCode and Rainbows? >> And with your approach, one has to test for type and downcast, or >> otherwise have multiple catch blocks (I don't want to miss plain >> FileNotFoundExceptions). So it's square one. >> >> With Variant[string] (or something equivalent, nothing better comes to mind) >> one does: >> >> >> try { >> ... >> } catch (FileNotFoundException ex) { >> if (ex.hasInfo(MyNameConstant)) { >> ... use that ... >> } >> ... common handling ... >> } >> >> >> --jm > > Regarding the downcast - you still perform a check in the code above! You gained nothing by replacing a type check with > a check on a hash. > > Regarding composition of several traits - even that simple snippet is enough: > throw new WithRainbows!withErrorCode!withFoobar!FileNotFoundException(...); > > That's without further design which could probably improve this further. |
February 21, 2012 Re: The Right Approach to Exceptions | |
|---|---|
Posted in reply to Walter Bright | Walter Bright wrote:
> On 2/18/2012 3:13 PM, Andrei Alexandrescu wrote:
>> On 2/18/12 4:26 PM, Jonathan M Davis wrote (abridged):
>> GetOptException
>> FlagArgumentMissingException
>> InvalidFlagArgumentException
>> UnknownFlagException
>> FileException
>> FileNotFoundException
>> NotFileException
>> NotDirException
>> AccessDeniedException
>>
>> I died inside a little.
>
> I think typed exceptions are a good idea, but something looks wrong with
> these.
>
> (Also, having a large number of exception types is going to produce a
> lot of program size bloat. Remember, for EVERY class type, you've got
> the vtbl[], the .init data, and the TypeInfo. Going to town on exception
> types can really add this up.)
I feel that druntime might be optimized.
1) I think most of the bloat comes from .init data. For example this
program:
class A
{
ubyte[1024 * 1024 * 10] tenMegabytes;
}
class B : A {}
class C : B {}
class D : C {}
void main() { }
compiles to 40 MB exe under windows. Every class has copy of the same 10
MB .init data.
I wonder why init is byte[] array. IMHO the better thing would be making
it byte[][], i.e. array of byte arrays. Then some derived parts may be
shared as byte array slices. This maybe little slower because it adds
another level of indirection.
By the way, current byte[] array solution doesn't speed up void
initializers, because it always overwrites void fields. This simple program:
import std.stdio;
class A { ubyte[8] test = void; }
void main() { writeln((new A()).test); }
always prints [0, 0, 0, 0, 0, 0, 0, 0].
With byte[][] array, some slices may have null ptrs so they may be
initialized using memset() or not initialized at all (when using void
initializer).
2) vtbl[] might also be shared for derivation chains that don't override
any functions. But that may slow down program start, because vtables
must be constructed at runtime.
Please correct me if I'm wrong :) I think that the use of classes should
not be limited because of implementation issues. For me it's purely
implementation issue, and I suppose it may be addressed with more
optimized ABI or druntime.
|
February 21, 2012 Re: Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions) | |
|---|---|
On Tue, Feb 21, 2012 at 03:54:30PM +0100, Artur Skawina wrote: > On 02/21/12 09:15, H. S. Teoh wrote: > > > > Sorry for this super-long post, but I wanted to lay my ideas out in a > > coherent fashion so that we can discuss its conceptual aspects without > > getting lost with arguing about the details. I hope this is a step in > > the right direction toward a better model of exception handling. > > I haven't read all of that huge trolli^Hbrainstorming thread, as most > of it was just bikeshedding - the exception hierarchy needs to just be > done; design by committee never works. It would have helped if you actually read all of it. :) I wasn't talking about the exception hierarchy at all. I was talking about how to recover from a given problem P without knowing where in the hierarchy P is, or even whether there is a hierarchy. Specifically, how high-level code 20 levels up the call stack can do something meaningful with a low-level error 20 levels down, without having to know anything about error codes, the context of the error, the state of variables, that sort of thing. And yet *still* have the low-level details sorted out once a decision is made. > You made some good points there, and had an interesting idea, so I'll > just comment on that. Credit where credit is due. This idea is not mine, it's the model they use in Common Lisp. I don't program in Lisp. I just read the article and found the idea interesting and possibly very useful in the context of D. The only thing I added, perhaps, is that instead of problem-specific conditions, as they appear to have in Lisp, I'm looking at generic categories of conditions, that you can handle from high-level code without ever needing to know the specifics of exactly what the condition is, and yet have the low-level code work it all out once you've made a decision. > > The try-catch mechanism is not adequate to implement all the > > recovery actions described above. As I've said before when > > discussing what I > > Imagine that catch blocks are delegates. Now, when something throws an > exception, the catch handler is looked up, just like right now, except > the stack isn't unwound, the matching catch-delegate is called. If it > returns 'X' control flow is returned to the 'throw' scope, so that the > failed operation can be retried. If it returns 'Y' then the search > for another matching catch block continues, that one is called and so > on. If a delegate returns 'Z' the stack is unwound and control is > passed to the code following this catch statement. It's not always as simple as "return control to throw scope", the point of not rolling back the stack is so that you can present a list of resolution alternatives to the catch block, and have it select one, then you proceed with that method of recovery. These resolution alternatives need to be standardized, since otherwise you pollute high-level code with low-level knowledge (suboperation Y023 20 levels down the call stack has failure mode F04 which has recovery options R078, R099, R132, so I can catch F04, then choose between R089, R099, R132), making it only able to deal with a single problem decided beforehand by the coder. What you want is to be able to say things like, given any transient problem, I don't care what exactly, retry 5 times then give up. Or, given any component failure, I don't know nor care which component, try an alternative component if one exists, otherwise give up. This way, if a new component is added to the low-level code, with brand new ways of failure, the high-level code can still handle the new failures, even though when it was written such problems didn't even exist yet. > Add a bit syntactic sugar, and the result could be something like > this: > > void f1(C c) { > retry: > if (!c.whatever()) > throw(retry) new WhateverEx("Help! XYZ failed", moreDetails, errorCodesEtc); > } > > void f2(C c) { > try { > f1(c); > } catch (WhateverEx e) { > if (isTransient(e)) { > doSomethingAndPray(); > continue; > } > if (nothingICanDo(e)) > throw; > if (iCanDealWithItHere(e)) > break; > } > /* ... */ > } > > Wouldn't this be enough to handle all of your cases? Not quite, but close. :) And this is a very nice syntax. > If exiting the scope maps to 'break' ie means > unwind-and-continue-from-here, it's even somewhat compatible with the > current scheme (you cannot jump with goto to inside the catch blocks, > but that's not a good idea anyway). > > Implementation question: could this be done w/o causing heap > allocation in most functions containing catch statements? [...] I've thought about using mixin templates to do insert labels to retry blocks and goto's in catch blocks, so no cost is incurred unless an error actually happens. Not sure if this is a good implementation method, though. T -- BREAKFAST.COM halted...Cereal Port Not Responding. -- YHL | |

Reply