www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - C++/D interface: exceptions

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Hello,


We are racking our brains to figure out what to do about exceptions 
thrown from C++ functions into D code that calls them.

A few levels of Nirvana would go like this:

0. Undefined behavior - the only advantage to this is we're there 
already with no work :o).

1. C++ exceptions may be caught only by C++ code on the call stack; D 
code does the stack unwinding appropriately (dtors, scope statements) 
but can't catch stuff.

2. D code can catch exceptions from C++ (e.g. via a CppException wrapper 
class) and give some info on them, e.g. the what() string if any.

Making any progress on this is likely to be hard work, so any idea that 
structures and simplifies the design space would be welcome.


Andrei
Sep 11 2014
next sibling parent reply "David Nadlinger" <code klickverbot.at> writes:
On Friday, 12 September 2014 at 00:34:52 UTC, Andrei Alexandrescu 
wrote:
 Making any progress on this is likely to be hard work, so any 
 idea that structures and simplifies the design space would be 
 welcome.
Just a quick comment on this: 2) is very simple to implement for all the compilers that actually use libunwind Dwarf 2 EH on Linux, i.e. GDC and LDC (I think deadalnix already looked into this for SDC). 3) is also doable, but of course significantly more annoying because you need to deal with the internals of the exception ABI of your C++ compiler. Accessing the exception object is relatively trivial, ABI and mangling support is slowly coming anyway, but OTOH handling the exception lifetime correctly could become somewhat of a headache. David
Sep 11 2014
next sibling parent "David Nadlinger" <code klickverbot.at> writes:
On Friday, 12 September 2014 at 00:44:10 UTC, David Nadlinger 
wrote:
 2) […] 3)
Aaand of course I missed the fact that your list in fact started at 0.
Sep 11 2014
prev sibling next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Friday, 12 September 2014 at 00:44:10 UTC, David Nadlinger 
wrote:
 3) is also doable, but of course significantly more annoying 
 because you need to deal with the internals of the exception 
 ABI of your C++ compiler. Accessing the exception object is 
 relatively trivial, ABI and mangling support is slowly coming 
 anyway, but OTOH handling the exception lifetime correctly 
 could become somewhat of a headache.
Yes, that is pretty why I limited myself to the "unwind properly but do not catch" option. This one would require to mess with the innards of various C++ runtime.
Sep 11 2014
parent reply Jacob Carlborg <doob me.com> writes:
On 12/09/14 05:25, deadalnix wrote:

 Yes, that is pretty why I limited myself to the "unwind properly but do
 not catch" option. This one would require to mess with the innards of
 various C++ runtime.
On 64bit Objective-C can catch C++ exceptions. But I don't think you can do anything with the exception, i.e. it uses the following catch syntax: catch(...) {} Would that be easier? -- /Jacob Carlborg
Sep 11 2014
next sibling parent reply "Sean Kelly" <sean invisibleduck.org> writes:
On Friday, 12 September 2014 at 06:56:29 UTC, Jacob Carlborg 
wrote:
 On 12/09/14 05:25, deadalnix wrote:

 Yes, that is pretty why I limited myself to the "unwind 
 properly but do
 not catch" option. This one would require to mess with the 
 innards of
 various C++ runtime.
On 64bit Objective-C can catch C++ exceptions. But I don't think you can do anything with the exception, i.e. it uses the following catch syntax: catch(...) {} Would that be easier?
I think the trick is setting up the stack frame in such a way that the C++ exception mechanism knows there's a catch block available at all. From there, we should be able to use the standard interface-to-class method to call virtual functions on the exception object, and hopefully the C++ runtime will handle cleanup for us. I imagine the easiest thing would be to find a platform where we already know how exceptions are thrown in C++ (DMC on Windows?) and figure out how to make it work in D. With inner functions and inline asm, I'm sure it's possible to make this work without compiler changes. I don't know whether the unwinding mechanism differs across compilers or even compiler versions for a particular platform though. This may all end up being a bit brittle.
Sep 12 2014
parent reply Marco Leise <Marco.Leise gmx.de> writes:
Am Fri, 12 Sep 2014 15:55:37 +0000
schrieb "Sean Kelly" <sean invisibleduck.org>:

 On Friday, 12 September 2014 at 06:56:29 UTC, Jacob Carlborg 
 wrote:
 On 64bit Objective-C can catch C++ exceptions. But I don't 
 think you can do anything with the exception, i.e. it uses the 
 following catch syntax:

  catch(...) {}

 Would that be easier?
I think the trick is setting up the stack frame in such a way that the C++ exception mechanism knows there's a catch block available at all. From there, we should be able to use the standard interface-to-class method to call virtual functions on the exception object, and hopefully the C++ runtime will handle cleanup for us.
What exception object? throw "bad things happened"; -- Marco
Sep 12 2014
next sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Fri, Sep 12, 2014 at 06:19:54PM +0200, Marco Leise via Digitalmars-d wrote:
 Am Fri, 12 Sep 2014 15:55:37 +0000
 schrieb "Sean Kelly" <sean invisibleduck.org>:
 
 On Friday, 12 September 2014 at 06:56:29 UTC, Jacob Carlborg 
 wrote:
 On 64bit Objective-C can catch C++ exceptions. But I don't 
 think you can do anything with the exception, i.e. it uses the 
 following catch syntax:

  catch(...) {}

 Would that be easier?
I think the trick is setting up the stack frame in such a way that the C++ exception mechanism knows there's a catch block available at all. From there, we should be able to use the standard interface-to-class method to call virtual functions on the exception object, and hopefully the C++ runtime will handle cleanup for us.
What exception object? throw "bad things happened";
[...] Yeah, in C++, you can throw *anything*. Including ridiculous things like `throw NULL;` or `throw 3.14159;`. There's no method for that! What we might end up doing, might be to wrap the C++ exception in a D exception that contains a pointer to the C++ type along with whatever type info we can glean from the C++ runtime. We probably won't be able to do much more than that. T -- Bomb technician: If I'm running, try to keep up.
Sep 12 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Friday, 12 September 2014 at 16:37:43 UTC, H. S. Teoh via 
Digitalmars-d wrote:
 On Fri, Sep 12, 2014 at 06:19:54PM +0200, Marco Leise via 
 Digitalmars-d wrote:
 Am Fri, 12 Sep 2014 15:55:37 +0000
 schrieb "Sean Kelly" <sean invisibleduck.org>:
 
 On Friday, 12 September 2014 at 06:56:29 UTC, Jacob Carlborg 
 wrote:
 On 64bit Objective-C can catch C++ exceptions. But I don't 
 think you can do anything with the exception, i.e. it uses 
 the following catch syntax:

  catch(...) {}

 Would that be easier?
I think the trick is setting up the stack frame in such a way that the C++ exception mechanism knows there's a catch block available at all. From there, we should be able to use the standard interface-to-class method to call virtual functions on the exception object, and hopefully the C++ runtime will handle cleanup for us.
What exception object? throw "bad things happened";
[...] Yeah, in C++, you can throw *anything*. Including ridiculous things like `throw NULL;` or `throw 3.14159;`. There's no method for that! What we might end up doing, might be to wrap the C++ exception in a D exception that contains a pointer to the C++ type along with whatever type info we can glean from the C++ runtime. We probably won't be able to do much more than that.
How about try { my_cpp_func(); } catch(CppException!(const(char)*) e) { writeln(e.payload.fromStringz()); } ? Btw, how does implicit conversion work with `catch` in C++? I.e., if you throw a `char*`, will it be caught when you catch `const char*`? This can not be handled easily with such a template, as we would need to catch both `CppException!(const(char)*)` and `CppException!(char*)`.
Sep 12 2014
next sibling parent Marco Leise <Marco.Leise gmx.de> writes:
Am Fri, 12 Sep 2014 17:57:39 +0000
schrieb "Marc Sch=C3=BCtz" <schuetzm gmx.net>:

 How about
=20
      try {
          my_cpp_func();
      } catch(CppException!(const(char)*) e) {
          writeln(e.payload.fromStringz());
      }
=20
 ?
=20
 Btw, how does implicit conversion work with `catch` in C++? I.e.,=20
 if you throw a `char*`, will it be caught when you catch `const=20
 char*`? This can not be handled easily with such a template, as=20
 we would need to catch both `CppException!(const(char)*)` and=20
 `CppException!(char*)`.
No, let's just stick to 1) please :p Not only const(char) vs. char might be an issue because C's const doesn't match D's const exactly, but also char can be defined to be literally anything that can hold at least 8 bits. http://msdn.microsoft.com/en-us/library/0d294k5z.aspx http://www.cplusplus.com/reference/climits/ http://en.wikipedia.org/wiki/C_data_types I know this argument could be made about interfacing with C++ in general, but in case of throwing we do not replicate the structure of the C++ side in D as we would do with declarations of structs and classes, so the types would be thrown from C++ and end up "raw" in D. I think this requires at least a D compiler flag to name the C++ compiler eco system to emulate: e.g. MSVC++, ICC, DMC, GCC. With that information you could then catch(what_the_c_compiler_understands_by_a_const_unsigned_char_ptr) or catch(what_the_c_compiler_understands_by_a_const_signed_char_ptr) NOT possible: catch(what_the_c_compiler_understands_by_a_const_char_ptr), because that would have been remapped by the C++ compiler to either one of the former (I think). The raw void* exception data for C char* may be presented inside the catch block as byte*, ubyte*, ushort* or whatever is known to match the C compiler specified on the command line. (I hope my assumption about how C++ compilers mangle exception types is not too far off.) --=20 Marco
Sep 12 2014
prev sibling next sibling parent Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 12 Sep 2014 19:00, "via Digitalmars-d" <digitalmars-d puremagic.com>
wrote:
 On Friday, 12 September 2014 at 16:37:43 UTC, H. S. Teoh via
Digitalmars-d wrote:
 On Fri, Sep 12, 2014 at 06:19:54PM +0200, Marco Leise via Digitalmars-d
wrote:
 Am Fri, 12 Sep 2014 15:55:37 +0000
 schrieb "Sean Kelly" <sean invisibleduck.org>:

 On Friday, 12 September 2014 at 06:56:29 UTC, Jacob Carlborg > wrote:
 On 64bit Objective-C can catch C++ exceptions. But I don't > >
think you can do anything with the exception, i.e. it uses > > the following catch syntax:
  catch(...) {}

 Would that be easier?
 I think the trick is setting up the stack frame in such a > way
that the C++ exception mechanism knows there's a catch > block available at all. From there, we should be able to > use the standard interface-to-class method to call virtual > functions on the exception object, and hopefully the C++ > runtime will handle cleanup for us.
 What exception object?

 throw "bad things happened";
[...] Yeah, in C++, you can throw *anything*. Including ridiculous things like `throw NULL;` or `throw 3.14159;`. There's no method for that! What we might end up doing, might be to wrap the C++ exception in a D exception that contains a pointer to the C++ type along with whatever type info we can glean from the C++ runtime. We probably won't be able to do much more than that.
How about try { my_cpp_func(); } catch(CppException!(const(char)*) e) { writeln(e.payload.fromStringz()); } ?
I'd vote no.
Sep 12 2014
prev sibling next sibling parent reply "Sean Kelly" <sean invisibleduck.org> writes:
On Friday, 12 September 2014 at 17:57:41 UTC, Marc Schütz wrote:
 Btw, how does implicit conversion work with `catch` in C++? 
 I.e., if you throw a `char*`, will it be caught when you catch 
 `const char*`? This can not be handled easily with such a 
 template, as we would need to catch both 
 `CppException!(const(char)*)` and `CppException!(char*)`.
I'm hoping it's simply a matter of sticking the right data in a lookup table and letting the C++ runtime figure out what the proper match is for us. D currently has a custom mechanism for throwing on non-Windows platforms, but it may be worth switching to the established C++ approach, provided we can do so without losing anything (and this is a big "if" given how we implicitly chain exceptions).
Sep 12 2014
parent "David Nadlinger" <code klickverbot.at> writes:
On Friday, 12 September 2014 at 22:50:50 UTC, Sean Kelly wrote:
 I'm hoping it's simply a matter of sticking the right data in a
 lookup table and letting the C++ runtime figure out what the
 proper match is for us.
I suggest familiarizing yourself with how libunwind works. Look up personality routines, or even just peruse GCC's libstdc++-v3/libsupc++/eh_personality.cc or Clang's libcxxabi/src/cxa_{exception, personality}.cpp.
 D currently has a custom mechanism for throwing on non-Windows
 platforms, but it may be worth switching to the established C++
 approach, provided we can do so without losing anything (and 
 this is a big "if" given how we implicitly chain exceptions).
s/D/DMD/. And yes, LDC does implement exception chaining.
Sep 12 2014
prev sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Friday, 12 September 2014 at 17:57:41 UTC, Marc Schütz wrote:
 How about

     try {
         my_cpp_func();
     } catch(CppException!(const(char)*) e) {
         writeln(e.payload.fromStringz());
     }

 ?

 Btw, how does implicit conversion work with `catch` in C++? 
 I.e., if you throw a `char*`, will it be caught when you catch 
 `const char*`? This can not be handled easily with such a 
 template, as we would need to catch both 
 `CppException!(const(char)*)` and `CppException!(char*)`.
This is really WAY harder that it seems. First, you got to understand C++'s RTTI so you can generate the correct code to match catch blocks. That mean the D compiler must be able to generate the RTTI symbols for them to link. Then you got to implement the runtime part of it, considering there is absolutely NO standardization at all, it is 100% dependent on implementations details of the C++ standard lib you are using. You got to retrieve the exception from an opaque pointer on a structure that is compiler/stdlib dependent and make sense of it. Then you got to understand the C++ RTTI information do the proper matching of C++ exceptions with catch blocks's type. Finally, you have to interface with the C++ runtime to do the proper call in order to make the C++ runtime aware of what D's doing. This part is more standardized, so that should be easier.
Sep 12 2014
prev sibling parent "Sean Kelly" <sean invisibleduck.org> writes:
On Friday, 12 September 2014 at 16:11:28 UTC, Marco Leise wrote:
 Am Fri, 12 Sep 2014 15:55:37 +0000
 schrieb "Sean Kelly" <sean invisibleduck.org>:

 On Friday, 12 September 2014 at 06:56:29 UTC, Jacob Carlborg 
 wrote:
 On 64bit Objective-C can catch C++ exceptions. But I don't 
 think you can do anything with the exception, i.e. it uses 
 the following catch syntax:

  catch(...) {}

 Would that be easier?
I think the trick is setting up the stack frame in such a way that the C++ exception mechanism knows there's a catch block available at all. From there, we should be able to use the standard interface-to-class method to call virtual functions on the exception object, and hopefully the C++ runtime will handle cleanup for us.
What exception object? throw "bad things happened";
Shouldn't matter. It's just a callback taking a different parameter type. Though I think it would be a fair limitation to say that D will only catch objects.
Sep 12 2014
prev sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Friday, 12 September 2014 at 06:56:29 UTC, Jacob Carlborg
wrote:
 On 12/09/14 05:25, deadalnix wrote:

 Yes, that is pretty why I limited myself to the "unwind 
 properly but do
 not catch" option. This one would require to mess with the 
 innards of
 various C++ runtime.
On 64bit Objective-C can catch C++ exceptions. But I don't think you can do anything with the exception, i.e. it uses the following catch syntax: catch(...) {} Would that be easier?
Yes, but still require the D runtime to interface with the C++ runtime, in order to restore the proper context.
Sep 12 2014
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 12/09/14 02:44, David Nadlinger wrote:

 Just a quick comment on this: 2) is very simple to implement for all the
 compilers that actually use libunwind Dwarf 2 EH on Linux, i.e. GDC and
 LDC (I think deadalnix already looked into this for SDC).
It would be nice if DMD could do the same for D exceptions.
 3) is also doable, but of course significantly more annoying because you
 need to deal with the internals of the exception ABI of your C++
 compiler. Accessing the exception object is relatively trivial, ABI and
 mangling support is slowly coming anyway, but OTOH handling the
 exception lifetime correctly could become somewhat of a headache.
Is handling the exception any more of a headache compared to only using C++? -- /Jacob Carlborg
Sep 11 2014
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Friday, 12 September 2014 at 00:34:52 UTC, Andrei Alexandrescu 
wrote:
 Hello,


 We are racking our brains to figure out what to do about 
 exceptions thrown from C++ functions into D code that calls 
 them.

 A few levels of Nirvana would go like this:

 0. Undefined behavior - the only advantage to this is we're 
 there already with no work :o).

 1. C++ exceptions may be caught only by C++ code on the call 
 stack; D code does the stack unwinding appropriately (dtors, 
 scope statements) but can't catch stuff.
This is what SDC does.
 2. D code can catch exceptions from C++ (e.g. via a 
 CppException wrapper class) and give some info on them, e.g. 
 the what() string if any.
This would require that druntime to be dependent on C++ runtime.
Sep 11 2014
prev sibling next sibling parent "Sean Kelly" <sean invisibleduck.org> writes:
On Friday, 12 September 2014 at 00:34:52 UTC, Andrei Alexandrescu 
wrote:
 We are racking our brains to figure out what to do about 
 exceptions thrown from C++ functions into D code that calls 
 them.
Allowing a C++ exception to propagate through D code is definitely possible. Catching a C++ exception would require some knowledge of how that particular C++ runtime throws exceptions though. Like I can see registering an exception handler for a C++ exception maybe somewhat similar to SEH, then prettying it up with syntactic sugar. It seems tricky but doable, at least at a glance.
Sep 11 2014
prev sibling next sibling parent reply Iain Buclaw via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 12 Sep 2014 01:35, "Andrei Alexandrescu via Digitalmars-d" <
digitalmars-d puremagic.com> wrote:
 Hello,


 We are racking our brains to figure out what to do about exceptions
thrown from C++ functions into D code that calls them.
 A few levels of Nirvana would go like this:

 0. Undefined behavior - the only advantage to this is we're there already
with no work :o).
 1. C++ exceptions may be caught only by C++ code on the call stack; D
code does the stack unwinding appropriately (dtors, scope statements) but can't catch stuff.

Libunwind + handling foreign exceptions you can do this.  And is beneficial
in that it works with any other languages that use libunwind, such as gccgo.

Iain
Sep 11 2014
parent Jacob Carlborg <doob me.com> writes:
On 12/09/14 07:01, Iain Buclaw via Digitalmars-d wrote:

 Libunwind + handling foreign exceptions you can do this.  And is
 beneficial in that it works with any other languages that use libunwind,
 such as gccgo.
And Objective-C. -- /Jacob Carlborg
Sep 11 2014
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 12/09/14 02:35, Andrei Alexandrescu wrote:
 Hello,


 We are racking our brains to figure out what to do about exceptions
 thrown from C++ functions into D code that calls them.

 A few levels of Nirvana would go like this:

 0. Undefined behavior - the only advantage to this is we're there
 already with no work :o).

 1. C++ exceptions may be caught only by C++ code on the call stack; D
 code does the stack unwinding appropriately (dtors, scope statements)
 but can't catch stuff.

 2. D code can catch exceptions from C++ (e.g. via a CppException wrapper
 class) and give some info on them, e.g. the what() string if any.

 Making any progress on this is likely to be hard work, so any idea that
 structures and simplifies the design space would be welcome.
1 and 2 would really be a help for the D/Objective-C integration as well since on 64bit is uses the same exception model as C++. -- /Jacob Carlborg
Sep 11 2014
prev sibling parent Marco Leise <Marco.Leise gmx.de> writes:
Am Thu, 11 Sep 2014 17:35:25 -0700
schrieb Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>:

 Hello,
 
 
 We are racking our brains to figure out what to do about exceptions 
 thrown from C++ functions into D code that calls them.
 
 A few levels of Nirvana would go like this:
 
 0. Undefined behavior - the only advantage to this is we're there 
 already with no work :o).
 
 1. C++ exceptions may be caught only by C++ code on the call stack; D 
 code does the stack unwinding appropriately (dtors, scope statements) 
 but can't catch stuff.
 
 2. D code can catch exceptions from C++ (e.g. via a CppException wrapper 
 class) and give some info on them, e.g. the what() string if any.
 
 Making any progress on this is likely to be hard work, so any idea that 
 structures and simplifies the design space would be welcome.
 
 
 Andrei
I would say aim for 1. I wouldn't expect any less or any more. Exception handling seems to have a platform wide standard on major OSs that D should follow (e.g. libunwind helps with this on GCC dominated systems), but dealing with C++'s "throw anything" seems overkill to me for the next milestone in C++ interop. After all there could be exceptions using multiple inheritance, templated objects or basic data types thrown from the C++ side. -- Marco
Sep 12 2014