digitalmars.D.learn - how to catch D Throwables (or exceptions) from C++?
- Timothee Cour via Digitalmars-d-learn (15/15) Nov 30 2016 eg:
- Nicholas Wilson (5/20) Nov 30 2016 portably not sure, but if you're using dwarf / libunwind you can
- =?UTF-8?B?UsOpbXkgTW91w6t6YQ==?= (94/109) Dec 01 2016 I had the a similar problem when writing bindings to the RtMidi
- Jacob Carlborg (49/64) Dec 02 2016 At least for a C++ exception it's possible to get the current exception
- Elie Morisse (9/76) Dec 02 2016 Exceptions thrown from D set a different exception class in the
eg: ``` dlib.d: extern(C) void dfun(){assert(0, "some_msg");} clib.cpp: extern "C" void dfun(); void fun(){ try{ dfun(); } catch(...){ // works but how do i get "some_msg" thrown from D? } } ```
Nov 30 2016
On Thursday, 1 December 2016 at 01:58:13 UTC, Timothee Cour wrote:eg: ``` dlib.d: extern(C) void dfun(){assert(0, "some_msg");} clib.cpp: extern "C" void dfun(); void fun(){ try{ dfun(); } catch(...){ // works but how do i get "some_msg" thrown from D? } } ```portably not sure, but if you're using dwarf / libunwind you can probably use the API directly and get a pointer to the exception object and dereference a field of it's (i.e. p+ Throwable.msg.offsetof ).
Nov 30 2016
On Thursday, 1 December 2016 at 01:58:13 UTC, Timothee Cour wrote:eg: ``` dlib.d: extern(C) void dfun(){assert(0, "some_msg");} clib.cpp: extern "C" void dfun(); void fun(){ try{ dfun(); } catch(...){ // works but how do i get "some_msg" thrown from D? } } ```I had the a similar problem when writing bindings to the RtMidi library back in 2013. I opted for catching C++ exceptions in C++, wrap the functions in a C API; the C API returns a special type that contains a status, a result and an error message. On the D side, when a status is false, a D exception is raised, mirroring the one that was caught in C++. This strategy is described in dconf 2014: https://www.youtube.com/watch?v=1JZNvKhA3mA&t=20m45s It should be working the other way arround: catch a D exception in D, return a wrapped value: on the C++ side, if the wrapped value status is false, throw an exception Below is an sample of the code I wrote: In C++: ``` * Special return type. * - success is true when a call went right, * is false when an exception occured. * - errMsg can be used to throw a D exception. * - value is the value to be returned from a call. */ template <typename T> struct answer { int success; T value; const char * errMsg; }; * Predefined types of return for RtMidi. */ typedef answer<RtMidiIn *> answerRtMidiIn_p; typedef answer<RtMidiOut *> answerRtMidiOut_p; typedef answer<bool> answerBool; typedef answer<const char *> answerConstChar_p; typedef answer<double> answerDouble; answerRtMidiIn_p RtMidiIn_new ( int api, char * clientName, unsigned int queueSizeLimit) { RtMidiIn * ptr; try { const std::string name = std::string (clientName); ptr = new RtMidiIn ((RtMidi::Api) api, name, queueSizeLimit); answerRtMidiIn_p ans = {true, ptr, ""}; return ans; } catch (RtError & error) { answerRtMidiIn_p ans = {false, 0, error.getMessage ().c_str ()}; return ans; } } ``` in D: ``` /* Special return type. * - success is true when a call went right, * is false when an exception occured. * - errMsg can be used to throw a D exception. * - value is the value to be returned from a call. */ struct answer (T) { int success; T value; const (char) * errMsg; } extern (C) { // ... answer!(void *) RtMidiIn_new ( int api, immutable(char) * clientName, uint queueSizeLimit); // ... } class RtMidiIn { // Pointer to the C++ class, package visibility. protected void * ptr; public: this ( int api = UNSPECIFIED, string clientName = "RtMidi Input Client", uint queueSizeLimit = 100) { answer!(void *) ans = RtMidiIn_new (api, clientName.toStringz, queueSizeLimit); if (! ans.success) throw new RtError (ans.errMsg.to!string); this.ptr = ans.value; } // ... } ```
Dec 01 2016
On 2016-12-01 02:58, Timothee Cour via Digitalmars-d-learn wrote:eg: ``` dlib.d: extern(C) void dfun(){assert(0, "some_msg");} clib.cpp: extern "C" void dfun(); void fun(){ try{ dfun(); } catch(...){ // works but how do i get "some_msg" thrown from D? } } ```At least for a C++ exception it's possible to get the current exception with __cxxabiv1::__cxa_current_primary_exception(). I verified and it works for C++ exceptions. I would think that the following works for D exceptions, but I cannot even catch the D exception in C++. Maybe it's not working properly on macOS. // c++ void foo(); const char* getExceptionMessage(void*); void bar() { try { foo(); } catch(...) { void* e = __cxxabiv1::__cxa_current_primary_exception(); if (e) { const char* msg = getExceptionMessage(e); if (msg) printf("%s\n", msg); else printf("no message\n"); } else { printf("no exception\n"); } } } // d extern(C++) void foo() { throw new Exception("foo"); } extern(C++) immutable(char)* getExceptionMessage(void* e) { if (e) { auto t = cast(Throwable) e; return t.msg.ptr; } return null; } I'm compiling the C++ code with clang++ and I need to link with libc++abi. -- /Jacob Carlborg
Dec 02 2016
On Friday, 2 December 2016 at 08:13:51 UTC, Jacob Carlborg wrote:On 2016-12-01 02:58, Timothee Cour via Digitalmars-d-learn wrote:Exceptions thrown from D set a different exception class in the header, and the C++ personality routine (i.e the function that gets called for each catch block) only handles exceptions which displays the C++ exception class, so let D exceptions slip through. To catch exceptions thrown by D code in C++ I think this would have to be done by throwing a C++ exception (eventually wrapping a D class), but this is yet to be implemented.eg: ``` dlib.d: extern(C) void dfun(){assert(0, "some_msg");} clib.cpp: extern "C" void dfun(); void fun(){ try{ dfun(); } catch(...){ // works but how do i get "some_msg" thrown from D? } } ```At least for a C++ exception it's possible to get the current exception with __cxxabiv1::__cxa_current_primary_exception(). I verified and it works for C++ exceptions. I would think that the following works for D exceptions, but I cannot even catch the D exception in C++. Maybe it's not working properly on macOS. // c++ void foo(); const char* getExceptionMessage(void*); void bar() { try { foo(); } catch(...) { void* e = __cxxabiv1::__cxa_current_primary_exception(); if (e) { const char* msg = getExceptionMessage(e); if (msg) printf("%s\n", msg); else printf("no message\n"); } else { printf("no exception\n"); } } } // d extern(C++) void foo() { throw new Exception("foo"); } extern(C++) immutable(char)* getExceptionMessage(void* e) { if (e) { auto t = cast(Throwable) e; return t.msg.ptr; } return null; } I'm compiling the C++ code with clang++ and I need to link with libc++abi.
Dec 02 2016