views:

685

answers:

2

I'm using swig to wrap a class from a C++ library with python. It works overall, but there is an exception that is thrown from within the library and I can't seem to catch it in the swig interface, so it just crashes the python application!

The class PyMonitor.cc describes the swig interface to the desired class, Monitor. Monitor's constructor throws an exception if it fails to connect. I'd like to handle this exception in PyMonitor, e.g.:

PyMonitor.cc:

#include "Monitor.h"  

// ...  

bool PyMonitor::connect() {  
    try {  
        _monitor = new Monitor(_host, _calibration);  
    } catch (...) {  
        printf("oops!\n");  
    }  
}

// ...

However, the connect() method never catches the exception, I just get a "terminate called after throwing ..." error, and the program aborts.

I don't know too much about swig, but it seems to me that this is all fine C++ and the exception should propagate to the connect() method before killing the program.

Any thoughts?

+1  A: 

I'm not familiar with swig, or with using C++ and Python together, but if this is under a recent version of Microsoft Visual C++, then the Monitor class is probably throwing a C structured exception, rather than a C++ typed exception. C structured exceptions aren't caught by C++ exception handlers, even the catch(...) one.

If that's the case, you can use the __try/__except keywords (instead of try/catch), or use the _set_se_translator function to translate the C structured exception into a C++ typed exception.

(Older versions of MSVC++ treated C structured exceptions as C++ int types, and are caught by C++ handlers, if I remember correctly.)

If this isn't under Microsoft Visual C++, then I'm not sure how this could be happening.

EDIT: Since you say that this isn't MSVC, perhaps something else is catching the exception (and terminating the program) before your code gets it, or maybe there's something in your catch block that's throwing another exception? Without more detail to work with, those are the only cases I can think of that would cause those symptoms.

Head Geek
Sorry, I should have specified, I'm just using the gnu toolchain, not MVC++. The exception being thrown from Monitor is a usual C++ exception, using the throw keyword on a c++ object.
chase
+1  A: 

It's possible that a function called directly or indirectly by the Monitor constructor is violating its exception specification and doesn't allow std::bad_exception to be thrown. If you haven't replaced the standard function for trapping this, then it would explain the behaviour that you are seeing.

To test this hypothesis you could try defining your own handler:

void my_unexpected()
{
    std::cerr << "Bad things have happened!\n";
    std::terminate();
}


bool PyMonitor::connect() {  

    std::set_unexpected( my_unexpected );

    try {  
        _monitor = new Monitor(_host, _calibration);  
    } catch (...) {  
        printf("oops!\n");  
    }  
}

If you get the "Bad things have happened!" error message then you have confirmed that this is the case, but unfortunately there may not be a lot that you can do. If you're 'lucky', you may be able to throw an exception from my_unexpected that is allowed by the exception specification of the function that is currently failing, but in any case your unexpected handler is not allowed to terminate normally. It must throw or otherwise terminate.

To fix this you really need to get into the called code and either correct it so that the exception specification is not violated, either by fixing the specification itself or by fixing the code so that it doesn't throw the exception that isn't expected.

Another possibility is that an exception is being thrown during stack unwinding caused by the original exception being thrown. This also would cause termination of the process. In this case, although you can replace the standard terminate function, you have no option but to abort the program. A terminate handler isn't allowed to throw or return, it must terminate the program.

Charles Bailey
That's an interesting idea. I tried using set_unexpected as above, and I was unable to catch it. However, when I tried something similar with set_terminate, I was able to 'gain some control' again, just printing out a message and calling abort.I'm beginning to think that swig is somehow letting PyMonitor and Monitor run in different process spaces or something, and the exception thrown in the Monitor constructor somehow unwinds to the top of its stack before getting into PyMonitor. Is this possible?
chase