在DLL边界上抛出异常这个“技巧”是一个坏主意吗?

时间:2015-06-10 15:09:20

标签: c++ exception dll

我正在构建一个共享库,我希望在不同的编译器(如Windows上的MSVC和GCC)之间兼容ABI。我的灵感来自this blog post。我唯一遗漏的是能够跨DLL边界抛出异常......所以我做了这个小技巧:

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...");
    }
}

并在客户端代码中

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
    }
}

这就像一个魅力。我可以在客户端代码中处理从DLL抛出的异常(事实上,由客户端代码抛出)。我所知道的唯一缺点是,在编译DLL时我有大量的警告,因为“并非所有控制路径都返回一个值”...

我在这里遗漏了什么吗?这真的是个好主意还是根本没有?

2 个答案:

答案 0 :(得分:1)

这可能有效,但前提是两个代码库的抛出机制和堆栈展开代码基本相同且兼容。

为了销毁它之后应该抛出的每个对象,客户端代码必须能够理解DLL代码如何设置其堆栈以及它注册要清理的析构函数的位置等。它可能是如果你幸运的话,你的客户端和DLL代码编译器足以同意这个工作的情况。

哪种方式破坏了你设计的重点。

可能有效的方法是仔细编组DLL边界上的异常。

" C" DLL的API返回有关抛出的异常(如果有)的信息。客户端编译的C ++头文件包装器调用" C" API,并在客户端编译的代码中检测异常,解开它并抛出它。

在DLL中," C" API执行try{}catch(){},并调用内部C ++库。 catch子句将异常信息填充到" C" API返回值,并返回。

现在抛出内部异常,在C ++ DLL中捕获,在" C"中的DLL边界上编组。 API,在客户端代码端打包回异常,并重新抛出。

答案 1 :(得分:0)

除非一切都在您的完全控制之下,否则在DLL边界内抛出异常通常是一个坏主意。

通过一切,我的意思是:投掷者和捕手应该使用相同的编译器版本,相同的标准库和相同的运行时实例。所有这些事情都可以摒弃异常处理 - 希望只是一次崩溃,但可能会带来更不可思议的微妙结果。