情况是我有一个用C ++编写的动态库,它由另一个动态库包装为Python,也是用C ++编写的(由SIP生成具体)。第一个动态库定义了一个函数do_raise
,它抛出异常RaiserError
,它是std::exception
的子类。包装do_raise
的第二个动态库试图捕获RaiserError
,以便将其转换为Python异常。
使用Visual C ++构建,一切都按预期工作,并且RaiserError
被正确捕获。但是,在Linux上使用g ++时,RaiserError
未被捕获。如果我尝试捕捉std::exception
而不是(RaiserError
的基类),它就可以了。这里出了什么问题?这两个库是否有RaiserError
类型的不同概念,因为它不会被catch块识别?
为了进行测试,我还编写了一个小的可执行文件,在C ++库中调用do_raise
,在这里我甚至可以用g ++来捕获RaiserError
。
答案 0 :(得分:3)
您无法跨模块边界可靠地传递C ++异常。在Windows上(至少对于32位进程),有一个标准的ABI,但是使用gcc,你运气不好。
答案 1 :(得分:2)
我相信GCC使用某种指向类型信息的指针,如果它与指针不匹配,那么类型就不一样了。
这听起来像是Microsoft C ++回归到字符串类型信息比较的情况之一(缓慢但有效)。这对GCC没有帮助。
您可以尝试确保不隐藏异常类,并且在ELF .so文件中可以看到typeinfo。尝试确保仅在一个.so。
中定义类型编辑:我只想到了这一点:我相信ELF动态链接器可以很好地使用typeinfo符号的多个副本,并且会选择使用一个副本。我认为如果您链接了一些静态代码或者在其中一个.so或可执行文件中使用-Bsymbolic,您可以获得多个副本。
答案 2 :(得分:1)
我之前没有遇到过.so抛出异常的问题,所以不能说出我在这里看到的其他回复。
与查找无关的东西是你的任何析构函数是否可能抛出异常:如果你在析构函数中抛出一个新的异常,同时展开抛出第一个异常的堆栈,你的程序将中止,独立于任何包装尝试/捕获。使用g ++,错误消息看起来就像没有捕获第一个异常一样。小心地逐步展开堆栈,看看是否可能发生这种情况。
答案 3 :(得分:0)
根据其他答案,问题是(更有可能)在.so中缺少RTTI信息。
尝试将-Wl,-E附加到编译器标志,这将导致链接器导出所需的类型信息。
VC ++使用字符串比较(而不是指针比较)来匹配类型(动态转换,捕获等),这在运行时较慢但导致较小的dll(因为在dll中导出的RTTI较少)。
只要您使用相同的编译器编译共享对象(或者使用供应商中立的ABI,如g ++> = 4.3),这将是可靠的。