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