<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On 11 November 2015 at 19:02, Andrei Alexandrescu via Digitalmars-d <span dir="ltr"><<a href="mailto:digitalmars-d@puremagic.com" target="_blank">digitalmars-d@puremagic.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span class="">On 11/11/2015 12:19 PM, Iain Buclaw via Digitalmars-d wrote:<br>
</span><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span class="">
On 11 November 2015 at 17:25, Andrei Alexandrescu via Digitalmars-d<br></span><span class="">
<<a href="mailto:digitalmars-d@puremagic.com" target="_blank">digitalmars-d@puremagic.com</a> <mailto:<a href="mailto:digitalmars-d@puremagic.com" target="_blank">digitalmars-d@puremagic.com</a>>> wrote:<br>
<br>
    On 11/11/2015 08:08 AM, David Nadlinger wrote:<br>
    [snip]<br>
<br>
    Regarding the top-level issue. Walter and I agree it's an important<br>
    problem, and also that plugging holes as they start leaking (which<br>
    is what we've been doing so far) is not going to work well in the<br>
    long haul.<br>
<br>
    A redesign of template instantiation is necessary, and Walter needs<br>
    to be fully involved in it. However, please give it time. Walter is<br>
    currently working full time on catching C++ exceptions from D code,<br>
    and as we all know the best way of getting many things done is to do<br>
    one thing at a given time and do it fully. It should take him at<br>
    least two weeks' time to get there.<br>
<br>
<br></span><span class="">
Please no.  I've done my fair share of investigating this in libunwind,<br>
and it just isn't worth it.<br>
</span></blockquote>
<br>
Could you please provide Walter more detail? What's not worth it - catching C++ exceptions from D code? -- Andrei<br>
<br>
</blockquote></div><br></div><div class="gmail_extra">The main problem for seamless support is having some way to generate the C++ typeinfo in D to allow work across boundaries.  However there is nothing stopping catch-all style exceptions from working.<br><br></div><div class="gmail_extra">Consider the following desired example (this works today with minimal library changes in gdc's libunwind library):<br>*****************************<br>#include <iostream><br><br>int compute(int a, int b)<br>{<br>  if (b == 0)<br>    throw "Division by 0 in C++!";<br><br>  return a / b;<br>}<br><br>void cpp_main()<br>{<br>  try {<br>     std::cout << compute(1, 0);<br>  } catch (...) {<br>    std::cout << "Unknown exception caught in C++, rethrowing..." << "\n";<br>    throw;<br>  }<br>}<br>*****************************<br><br></div><div class="gmail_extra">Then put in bindings and main in D.<br>*****************************<br>import std.stdio;<br><br>extern(C++) void cpp_main();<br><br>void main()<br>{<br>    try {<br>        cpp_main();<br>    }<br>    catch {<br>        writeln("Unknown exception caught in D, exiting...");<br>    }<br>}<br>*****************************<br><br></div><div class="gmail_extra">In our unwind library, we can put in a copy of the C++ unwind implementation:<br>*****************************<br>static if (GNU_ARM_EABI_Unwinder)<br>{<br>  const _Unwind_Exception_Class __gxx_exception_class<br>  = ['G', 'N', 'U', 'C', 'C', '+', '+', '\0'];<br>}<br></div><div class="gmail_extra">else<br>{<br>  const _Unwind_Exception_Class __gxx_exception_class = 0x474e5543432b2b00UL;<br>}<br><br>// Structure of a C++ exception, represented as a C structure...<br>// See unwind-cxx.h for the full definition.<br>struct __cxa_exception<br>{<br>  void *exceptionType;<br>  void function(void *) exceptionDestructor;<br>  void function() unexpectedHandler;<br>  void function() terminateHandler;<br>  __cxa_exception *nextException;<br>  int handlerCount;<br><br>  static if (GNU_ARM_EABI_Unwinder)<br>  {<br>    __cxa_exception* nextPropagatingException;<br>    int propagationCount;<br>  }<br>  else<br>  {<br>    int handlerSwitchValue;<br>    const ubyte *actionRecord;<br>    const ubyte *languageSpecificData;<br>    _Unwind_Ptr catchTemp;<br>    void *adjustedPtr;<br>  }<br><br>  _Unwind_Exception unwindHeader;<br>}<br></div><div class="gmail_extra">*****************************</div><div class="gmail_extra"><br></div><div class="gmail_extra">Ignoring the main guts of gdc's unwind implementation (should be very similar in ldc and sdc by coincidence), let's instead focus on the exact part where we try to match the exception object, you'd have something like the following:<br>*****************************<br>if (ar_filter > 0)<br>  {<br>    // Positive filter values are handlers.<br>    void *catch_type = get_ttype_entry (&info, ar_filter);<br><br>    if (!foreign_exception)<br>      {<br></div><div class="gmail_extra">        if (_d_isbaseof (xh.object.classinfo, cast(ClassInfo)catch_type))<br>          saw_handler = true;<br>      }<br></div><div class="gmail_extra">    else<br>      {<br></div><div class="gmail_extra">        // == Case A ==<br></div><div class="gmail_extra">        // Null catch type is a catch-all handler; we can catch foreign exceptions with this.<br></div><div class="gmail_extra">        if (catch_type is null)<br>          saw_handler = true;<br><br>        else if (ue_header.exception_class == __gxx_exception_class)<br>          {<br>            // == Case B ==<br>            // Typeinfo are directly compared, which might not be correct if they aren't merged.<br>            void *except_typeinfo = ((cast(__cxa_exception *)(ue_header + 1)) - 1).exceptionType;<br><br>            if (catch_type == except_typeinfo)<br>              saw_handler = true;<br><br>            // == Case C ==<br>            // Typeid are compared between catch type and some specially crafted C++ Exception class.<br></div><div class="gmail_extra">            if (cast(ClassInfo)catch_type is typeid(UnknownCPPException))<br>              saw_handler = true;<br></div><div class="gmail_extra">          }<br>      }<br>  }<br>*****************************<br><br></div><div class="gmail_extra">Now let's discuss the three cases:<br><br></div><div class="gmail_extra">- Case A:<br></div><div class="gmail_extra">A `null` catch type never happens in D code, because `catch { }` is always re-written to `catch(Throwable) { }` (this might have changed since 2.066, but I doubt it).  So any kind of `catch(...)` equivalent in D would make this is a valid candidate for catching not just C++ exceptions, but also exceptions in any language that use libunwind for EH (Ada, Go, Java, etc...).  Bonus, we don't need to know anything about C++ exception objects or typeinfo to achieve this.<br></div><div class="gmail_extra"><br></div><div class="gmail_extra">In the other cases, checking the exception class is simple enough, libunwind provides a 
field for that, and just requires to declare it somewhere on the D 
bindings side.<br></div><div class="gmail_extra"><br></div><div class="gmail_extra">- Case B:</div><div class="gmail_extra">Explicitly try to match a C++ exception object.  Copying the __cxa_exception struct from unwind-cxx.h, 
takes advantage of ABI compatibility - though we must take care that 
they are kept in sync (not that it ever changes in an incompatible way).  Also from the C++ unwind library, is one small trick to get the C++ typeinfo pointer.  Where this falls short is that ``catch_type == except_typeinfo`` will always be false unless we expose some way to generate C++ typeinfo in D.  *Maybe* this could be achieved by generating an pointer to an `extern(C++)` symbol to the typeinfo - just need to get mangling right.  But this is the ugliest thing we can possibly do.<br><br></div><div class="gmail_extra">- Case C:<br></div><div class="gmail_extra">An alternative to the catch all handler in case A, but instead test whether the catch we are inspecting matches some explicit object (derived from `Throwable` to be compatible with the existing compiler constraints).  Again, this can be extended for other languages (UnknownAdaException, UnknownGoException, UnknownJavaException, etc...) - however, unlike case A, we need to copy the exception class signature of other languages into the D bindings.<br><br></div><div class="gmail_extra">With the possible exception being in case B, there is no way to return the foreign language object from EH to the catch handler.  Using case C and storing the caught exception object to a variable `catch (UnknownCPPException e)` will always result with the object being equal to `null`.<br></div><div class="gmail_extra"><br>*****************************<br></div><div class="gmail_extra"><br>Coming back round full circle to the original example, I've implemented case C but instead using `Throwable` to make `catch { }` succeed in catching C++ exceptions.<br><br></div><div class="gmail_extra">Compilation and Result:<br></div><div class="gmail_extra">$ gdc dcode.d cxxcode.cc<br>$ ./a.out<br>Unknown exception caught in C++, rethrowing...<br>Unknown exception caught in D, exiting...<br><br></div><div class="gmail_extra">Even throwing in D, and catching in C++ works thanks to C++'s existing support for foreign language exceptions and `catch(...)`.  So moving `compute` into D, we have the following.<br>*****************************<br>extern(C++) int compute(int a, int b)<br>{<br>  if (b == 0) {<br>    writeln("Exception thrown in D...");<br>    throw new Exception("Division by 0 in D!");<br>  }<br><br>  return a / b;<br>}<br>*****************************<br></div><div class="gmail_extra"><br></div><div class="gmail_extra"><div class="gmail_extra">Compilation and Result:<br></div>$ gdc dcode.d cxxcode.cc<br>$ ./a.out<br>Exception thrown in D...<br>Unknown exception caught in C++, rethrowing...<br>Unknown exception caught in D, exiting...<br><br></div><div class="gmail_extra"><br></div><div class="gmail_extra"></div><div class="gmail_extra">Andrei - Hope this answers your question.<br><br>--<br></div><div class="gmail_extra">Regards<br></div><div class="gmail_extra">Iain<br></div></div>