March 08, 2013
On Friday, March 08, 2013 15:46:30 Jonathan M Davis wrote:
> That's the way that it's supposed to be done. I don't know why anyone would mess around with typeid that.

"with typeid _like_ that."

My fingers don't seem to keep up with my brain very well...

- Jonathan M Davis
March 08, 2013
On 3/8/13, Rob T <alanb@ucora.com> wrote:
> So this is more efficient or has some other advantages than using typeid?

Benchmark! :)

Also you might find this useful some day: http://wiki.dlang.org/Dispatching_an_object_based_on_its_dynamic_type
March 08, 2013
On Friday, 8 March 2013 at 21:04:06 UTC, Rob T wrote:
> On Friday, 8 March 2013 at 20:46:42 UTC, Jonathan M Davis wrote:
>> On Friday, March 08, 2013 11:56:12 H. S. Teoh wrote:
>>> On Fri, Mar 08, 2013 at 08:52:21PM +0100, Rob T wrote:
>>> > On Friday, 8 March 2013 at 06:05:02 UTC, Maxim Fomin wrote:
>>> > > catch (Exception e) {
>>> > > 
>>> > > if (typeid(e) == typeid(myException1))
>>> > > 
>>> > > throw e; // may be downcasted, if necessary
>>> > > 
>>> > > // to work with specific fields
>>> > > 
>>> > > }
>>> > 
>>> > Isn't it better to check identity in this way?
>>> > 
>>> > if (typeid(e) is typeid(myException1))
>>> 
>>> [...]
>>> 
>>> Couldn't you just do this?
>>> 
>>> auto myExc = cast(myException1)e;
>>> if (myExc !is null) {
>>> // do stuff with myExc
>>> }
>>> 
>>> ?
>>> 
>>> Or am I missing the point here?
>>
>> That's the way that it's supposed to be done. I don't know why anyone would
>> mess around with typeid that.
>>
>> - Jonathan M Davis
>
> So this is more efficient or has some other advantages than using typeid?
>
> if ( cast(myException1)e !is null )
> {
>    // do stuff with myException1
> }
> else
> if ( cast(myException2)e !is null )
> {
>    // do stuff with myException2
> }
> else
> if ( cast ...
>
> --rt

Please read comments as well as code - they were important.

typeid(e) == typeid(myException) is useful for simple query (identity operator is better) and costs here is call to opEquals (with identity operator there is simple comparison)

Casting leads to call to _d_isbaseof2() rt function which is slower than the first, but it can be a necessity.
March 08, 2013
On 03/08/2013 11:26 AM, Rob T wrote:
> On Friday, 8 March 2013 at 17:40:38 UTC, Ali Çehreli wrote:
>> On 03/08/2013 12:39 AM, Rob T wrote:
>> > In C++ you can do this
>> >
>> > std::exception Trace()
>> > {
>> > try
>> > {
>> > // key item missing from D
>> > throw; // <= rethrow last exception
>>
>> This idiom is know as a Lippincott Function.
>>
>> Ali
>
> I'm having trouble finding references on Lippincott Function, so if you
> have any more information, please let me know. Thanks.
>
> --rt

I heard about this idiom for the first time in Jon Kalb's talk. The name appears in his slides:

  http://exceptionsafecode.com/

Very smart way of writing a single function that catches many different types of exceptions; does special things about them, like producing special error codes, and then returns those codes to the C world.

I am familiar with your use case: When a C layer calls C++, the exceptions must be caught and converted to error codes. This is how we do it:

#define BEGIN_C_INTERFACE try {

#define END_C_INTERFACE                                     \
} catch (const SomeType & e) {                              \
  // ... special code for SomeType                          \
} catch (const SomeOtherType & e) {                         \
  // ... special code for SomeOtherType                     \
                                                            \
// ... etc.                                                 \
                                                            \
} catch( ... ) {                                            \
    log("Unhandled exception");                             \
    return some_generic_error_code;                         \
}

Then our C++ functions that are called from C are like this:

extern "C"
int my_api_func()
{
    BEGIN_C_INTERFACE

    // ... the actual body of the function

    END_C_INTERFACE
}

Lippincott functions avoid macros and make it more explicit that the entire body is inside a try block.

Ali
March 08, 2013
On Friday, March 08, 2013 20:16:13 Rob T wrote:
> On Friday, 8 March 2013 at 18:49:53 UTC, Jonathan M Davis wrote:
> > Except that the C++ one is just as pointless. In both cases,
> > you're telling it
> > to catch everything. It's just that syntax is slightly
> > different, because D
> > doesn't allow you to throw without an explicit variable. And
> > it's only a
> > handful of characters difference in length. So, to some of us
> > at least, it
> > seems like you're blowing things out of proportion. And given
> > the lack of
> > clarity in the C++ solution, it comes off as being worse from a
> > technical
> > perspective, regardless of the typing involved.
> > 
> > - Jonathan M Davis
> 
> I don't wish to blow things out of proportion, and will say again that the main objective I was trying to achieve has been met, thanks to the assistance I received in here, so this remaining item is not all that major, it's just an unnecessary repetitive nuisance to me.
> 
> From a technical stand point, I'm implementing a reusable exception handler or dispatcher (I've seen these two terms used to describe it) which is extremely useful to me, and I would assume to many others. There are examples of this concept implemented in C++ by other programmers, that's how I got the idea.
> 
> Before I started using an exception handler, my exception handling was very limited and tedious to implement, and I never saw a need to re-throw an exception. There may be other uses for rethrow that I'm not aware of.
> 
> What my C++ exception handler does not require, is the exception reference passed in as an argument, so that's one of the main differences between what D allows and what C++ allows. In my case every catch statement and function call will be identical, it's slightly more tedious to type in than the C++ version. No big deal for you, but it is annoying for me because I use this form very frequently.
> 
> If you know of a better way to implement an exception handler in D, then I'd like to know about it. For example I do know that D's system allows you to insert callback functions, but I don't yet know how to make use out of it, so perhaps there's a better way.
> 
> Any further help or insight on this matter is appreciated.

Well, ultimately, it looks like the only difference between C++ and D on this, would be that

std::exception Trace()
{
 try
 {
 // key item missing from D
 throw; // <= rethrow last exception
 }
 catch( EType1 &E )
 {
 // do stuff
 return new std::exception( ...
 }
 catch( EType2 &E )
 {
 // do different stuff
 return new std::exception( ...
 }
 catch(...)
 {
 // unkown type, do something else
 throw new std::exception( ...
 }

}

would become

Exception Trace(Exception e)
{
 try
 {
 // key item missing from D
 throw e; // <= rethrow last exception
 }
 catch( EType1 &e )
 {
 // do stuff
 return new std::exception( ...
 }
 catch( EType2 &e )
 {
 // do different stuff
 return new std::exception( ...
 }
 catch(...)
 {
 // unkown type, do something else
 throw new std::exception( ...
 }

}

or this

Exception Trace(Exception e)
{
 if(cast(Etype1)e)
 {
 // do stuff
 return new Exception( ...
 }
 else if(cast(Etype2)e)
 {
 // do different stuff
 return new Exception( ...
 }
 else
 {
 // unkown type, do something else
 throw new Exception( ...
 }
}

And in the caller,

try {...}
catch(...)
{
 throw Trace();
}

would become

try {...}
catch(Exception e)
{
 throw Trace(e);
}

So, ultimately, the code is nearly identical, and if you use casts, you can actually make Trace shorter and more efficient (since it avoids the extra throw). And so while D may not improve on the C++ code much, it seems to me that you can do pretty much exactly the same thing in both. There are differences, but they're quite minimal.

- Jonathan M Davis
March 08, 2013
On Friday, March 08, 2013 22:04:03 Rob T wrote:
> So this is more efficient or has some other advantages than using typeid?
> 
> if ( cast(myException1)e !is null )
> {
> // do stuff with myException1
> }
> else
> if ( cast(myException2)e !is null )
> {
> // do stuff with myException2
> }
> else
> if ( cast ...

I'd have to benchmark it to be 100% sure, but I'd be very surprised if using typeid were faster. It involves getting the typeid (which may mean making a function call), and I believe that typeid gives you a struct, which then gets compared with opEquals. Contrast this with the cast which should be able to just look at the vtbl and then set then do the cast if it can or result in null if it can't.

Regardless, as I understand it, casting is the "official" way to check whether a object is an instance of a particular type.

Also, you can actually make the ifs even shorter if you want to

if(cast(MyException1)e)
{...}
else if(cast(MyException2)e)
{...}

as using a class reference directly in a condition will check whether it's null.

- Jonathan M Davis
March 11, 2013
On Friday, 8 March 2013 at 21:10:15 UTC, Andrej Mitrovic wrote:
> On 3/8/13, Rob T <alanb@ucora.com> wrote:
>> So this is more efficient or has some other advantages than using
>> typeid?
>
> Benchmark! :)
>
> Also you might find this useful some day:
> http://wiki.dlang.org/Dispatching_an_object_based_on_its_dynamic_type

Very interesting. Thanks!

--rt
March 11, 2013
On Friday, 8 March 2013 at 22:10:19 UTC, Ali Çehreli wrote:
> I heard about this idiom for the first time in Jon Kalb's talk. The name appears in his slides:
>
>   http://exceptionsafecode.com/
>
> Very smart way of writing a single function that catches many different types of exceptions; does special things about them, like producing special error codes, and then returns those codes to the C world.

Thanks for the link and info!

> I am familiar with your use case: When a C layer calls C++, the exceptions must be caught and converted to error codes. This is how we do it:
>
> #define BEGIN_C_INTERFACE try {
>
> #define END_C_INTERFACE                                     \
> } catch (const SomeType & e) {                              \
>   // ... special code for SomeType                          \
> } catch (const SomeOtherType & e) {                         \
>   // ... special code for SomeOtherType                     \
>                                                             \
> // ... etc.                                                 \
>                                                             \
> } catch( ... ) {                                            \
>     log("Unhandled exception");                             \
>     return some_generic_error_code;                         \
> }
>
> Then our C++ functions that are called from C are like this:
>
> extern "C"
> int my_api_func()
> {
>     BEGIN_C_INTERFACE
>
>     // ... the actual body of the function
>
>     END_C_INTERFACE
> }
>
> Lippincott functions avoid macros and make it more explicit that the entire body is inside a try block.
>
> Ali

You seem to understand the use case, but why is your example not making use of it?

I was expecting something like this ...

int errFromException()
{
   try
   {
      throw;
   }
   catch (const SomeType & e)
   {
      // ... special code for SomeType
      return errSomeType;

   }
   catch (const SomeOtherType & e)
   {
      // ... special code for SomeOtherType
      return errSomeOtherType;
   }
   catch ( ...
   {

   ... etc

   catch( ... )
   {
       log("Unhandled exception");
       return some_generic_error_code;
   }

}

extern "C"
int my_api_func()
{
   try
   {
       // body of my_api_func
       return noerror;
   }
   catch(...)
   {
       return errFromException();
   }
}


No more ugly macros and you have the ability to custom fit the try catch block to perform any unique things that the function may require when an exception is thrown.

--rt
March 11, 2013
>>
>> Lippincott functions avoid macros and make it more explicit that the entire body is inside a try block.
>>
>> Ali
>

Oops, I think you were showing me an example where it could be used.

--rt
March 11, 2013
On 03/10/2013 08:10 PM, Rob T wrote:
>>>
>>> Lippincott functions avoid macros and make it more explicit that the
>>> entire body is inside a try block.
>>>
>>> Ali
>>
>
> Oops, I think you were showing me an example where it could be used.

Yes but we still use the macros! The macros were written ten years ago but I learned about this idiom two months ago. :)

Ali