Change the name of ArrayBoundsException in druntime
Steven Schveighoffer
schveiguy at yahoo.com
Thu Oct 23 13:02:22 PDT 2008
"Andrei Alexandrescu" wrote
> Steven Schveighoffer wrote:
>> "Andrei Alexandrescu" wrote
>>> Sean Kelly wrote:
>>>> Andrei Alexandrescu wrote:
>>>>> Robert Fraser wrote:
>>>>>> Option B:
>>>>>> ---------
>>>>>> try
>>>>>> {
>>>>>> new Socket(30587);
>>>>>> }
>>>>>> catch(Exception e)
>>>>>> {
>>>>>> if(e.type == ExceptionType.Socket)
>>>>>> printf("Could not open socket\n");
>>>>>> else
>>>>>> throw e;
>>>>>> }
>>>>> I think you'd be hard-pressed to justify the "if" inside the second
>>>>> example. You couldn't create a Socket, period. It doesn't matter where
>>>>> exactly the exception was generated from.
>>>>>
>>>>> That's one thing about large exception hierarchies: everybody can come
>>>>> with cute examples on how they could be useful. As soon as the rubber
>>>>> hits the road, however, differentiating exceptions by type becomes
>>>>> useless.
>>>> It may be different in a user application, but in services it's fairly
>>>> common to have specialized code for handling different exception types.
>>>> And more importantly, it's common to want different exception types to
>>>> propagate to different levels for handling. Sure, one could use a
>>>> generic exception handler at each level that rethrows if the detected
>>>> type isn't one that handler cares about but why do this when filtering
>>>> on type is a language feature?
>>>>
>>>> For example, let's say I have a network service that's backed by a SQL
>>>> database. My main program loop may look something like this:
>>>>
>>>> bool connected = false;
>>>> while( true )
>>>> {
>>>> try
>>>> {
>>>> while( true )
>>>> {
>>>> auto r = acceptRequest();
>>>> scope(failure) r.tellFailed();
>>>> if( !connected )
>>>> connectToDB();
>>>> handleRequest( r );
>>>> }
>>>> }
>>>> catch( SqlException e )
>>>> {
>>>> connected = false;
>>>> log( e );
>>>> }
>>>> catch( Exception e )
>>>> {
>>>> log( e );
>>>> }
>>>> }
>>>>
>>>> ...
>>>>
>>>> void handleRequest( Request r )
>>>> {
>>>> scope(failure) r.tellFailed( "General error" );
>>>>
>>>> try
>>>> {
>>>> // process r
>>>> }
>>>> catch( AuthException e )
>>>> {
>>>> r.tellFailed( "Authentication failure" );
>>>> }
>>>> catch( ValidationException e )
>>>> {
>>>> r.tellFailed( "Invalid request format" );
>>>> }
>>>> }
>>>>
>>>> Being able to trap specific types of exceptions makes this code cleaner
>>>> and more succinct than it would be otherwise. If this weren't possible
>>>> I'd have to trap, check, and rethrow certain exceptions at different
>>>> levels to ensure that the proper handler saw them.
>>> Thanks for fueling my argument. There's duplication in code examples, as
>>> in many other examples I've seen in favor of by-type handling.
>>>
>>> First example:
>>>
>>> catch( Exception e )
>>> {
>>> if (e.origin = "sql") connected = false;
>>> log( e );
>>> }
>>>
>>> Less code and no duplication. Second example is even starker:
>>>
>>> catch( AuthException e )
>>> {
>>> r.tellFailed( e.toString );
>>> }
>>>
>>> Clearly the need is to factor in the message to print in the exception,
>>> at least in this case and many like it.
>>
>> For the second example, you are assuming that ValidationException is a
>> subclass of AuthException.
>
> Sorry, I meant to catch Exception. My point was, if I want to print an
> informative message, the exception should be able to provide it without
> having to encode it in its type.
Sure, but then your example becomes:
catch(Exception e)
{
if(e.reason is Auth || e.reason is Validation)
r.tellFailed(e.toString());
else
throw e;
}
Not that it looks terrible, but I just want it to be clear that your
one-liner isn't sufficient.
>
>> I think in this case, ValidationException and AuthException might share a
>> common parent (SqlException) which you do NOT want to catch. It is
>> advantageous in this case to have both a hierarchy through inheritance
>> and a hierarchy through parameters.
> >
>> Here's another idea, to avoid rethrows, what about some pre-handler code
>> that tests if an exception is what you want? Perhaps the same mechanism
>> that is used by template constraints:
>>
>> catch(SqlException e) if (e.reason is Auth || e.reason is Validation)
>> {
>> r.tellFailed(e.toString());
>> }
>
> I think we're a bit too immersed in the "what can we add to the language".
> In this case, for example, I see no reason for the "if" to be moved inside
> and call it a day.
A fair point. It might help in situations like this (with your philosophy
of not subclassing for specific reasons):
try
{
try
{
...
}
catch(SqlException sql)
{
if(sql.reason == Auth || sql.reason == Validation)
changeUsertokensAndRetry(); // recovery
else
throw sql;
}
}
catch(Exception e)
{
r.tellFailed(e.toString());
}
Would then become:
try
{
...
}
catch(SqlException sql) if (sql.reason == Auth || sql.reason == Validation)
{
changeUsertokensAndRetry();
}
catch(Exception e)
{
r.tellFailed(e.toString());
}
instead of one giant catch block that catches all exceptions.
Basically, it augments the code that determines where the exception is
caught to check for more than just type.
But I don't know how common stuff like that is.
-Steve
More information about the Digitalmars-d
mailing list