Florian
Florian

Reputation: 115

Is this "trick" to throw exceptions across DLL boundaries a bad idea?

I am building a shared library that I want to be ABI compatible between different compilers (like MSVC and GCC on Windows). I took my inspiration from this blog post. The only thing that I missed was the ability to throw exceptions across the DLL boundaries... So I made this little trick :

MyLibrary.hpp

class Thrower{
    static void(*m_thowFunc)(const char*);

public:
    static void setThrowFunction(void(*func)(const char*)){
        m_thowFunc = func;
    }

    static void throwException(const char* msg){
        m_thowFunc(msg);
    }
};

extern "C" {
    EXPORT void MyLibrary_setThrowFunction(void(*func)(const char*));
    EXPORT void MyLibrary_foo();
}

MyLibrary.cpp

extern "C" {
    EXPORT void MyLibrary_setThrowFunction(void(*func)(const char*)){
        Thrower::setThrowFunction(func);
    }

    EXPORT void MyLibrary_foo(){
        Thrower::throwException("oops, an error occured...");
    }
}

and in the client code

void defaultThrowFunction(const char* msg){
    throw std::runtime_error(msg);
}

int main(){
    MyLibrary_setThrowFunction(&defaultThrowFunction);
    try{
        MyLibrary_foo();
    }catch(std::runtime_error& e){
        //handle exception
    }
}

This works like a charm. I can handle exceptions thrown from the DLL (in fact, thrown by the client code) in the client code. The only downside that I am aware of is that I have tons of warnings while compiling the DLL since "not all control path return a value"...

Am I missing something here ? Is this really a good idea or not at all ?

Upvotes: 3

Views: 3222

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275976

This may work, but only if the throwing mechanism and stack unrolling code of the two code bases is basically identical and compatible.

In order to destroy each of the objects it should after that throw, the client code has to be able to understand how the DLL code sets up its stack and where it registers destructors to be cleaned up, etc. It may be the case that your client and DLL code compilers are sufficiently in agreement for this to work, if you are lucky.

Which sort of ruins the point of your design.


An approach that might work is to carefully marshal the exceptions over the DLL boundary.

The "C" API for your DLL returns information about what exceptions (if any) where thrown. A C++ header-file only wrapper that the client compiles calls the "C" API, and in client-compiled code detects the exception, unwraps it, and throws it.

Inside the DLL, the "C" API does a try{}catch(){}, and calls the internal C++ library. The catch clause populates the exception information into the "C" API return value, and returns.

Now internal exceptions are thrown, caught within the C++ DLL, marshaled over the DLL-boundary in a "C" API, packaged back into exceptions on the client-code side, and rethrown.

Upvotes: 2

Cory Nelson
Cory Nelson

Reputation: 30021

Throwing exceptions across DLL boundaries is a bad idea in general unless everything is under your complete control.

By everything, I mean: the thrower and the catcher should use the same compiler version, the same standard library, and the same runtime instance. All of these things can throw off exception handling -- hopefully with just a crash, but maybe with a more unfortunately subtle result.

Upvotes: 1

Related Questions