Thread overview
how to catch D Throwables (or exceptions) from C++?
Dec 01, 2016
Timothee Cour
Dec 01, 2016
Nicholas Wilson
Dec 01, 2016
Rémy Mouëza
Dec 02, 2016
Jacob Carlborg
Dec 02, 2016
Elie Morisse
November 30, 2016
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?
  }
}
```


December 01, 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 ).
December 01, 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;
    }

    // ...
}
```


December 02, 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
December 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:
>> 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.

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.