2011-05-24 95 views
10

我偶然发现了一个我无法解决的问题。我正在编译一个共享库,其中包含一个模板类(Derived<T>,其基数为Base)以及该类的一些显式实例。我希望图书馆用户能够从这个模板类扩展。当我尝试dynamic_cast用户的实例从Base*Derived<T>*时,问题就出现了。在共享库中显式实例化模板化类和dynamic_cast

我缩小了问题该MWE:

共享库包含以下文件:

Base.h

#ifndef BASE_H_ 
#define BASE_H_ 

class Base { 
public: 
    Base(); 
    virtual ~Base(); 
}; 

#endif /* BASE_H_ */ 

Derived.h

#ifndef DERIVED_H_ 
#define DERIVED_H_  
#include <Base.h> 

template <typename T> 
class Derived : public Base { 
public: 
    Derived(); 
    virtual ~Derived(); 
}; 

#endif /* DERIVED_H_ */ 

Derived.cpp

#include <Derived.h> 

template <typename T> 
Derived<T>::Derived() : 
    Base() { 
} 

template <typename T> 
Derived<T>::~Derived() { 
} 

// explicit instantiations 
template class Derived<float>; 
template class Derived<double>; 
template class Derived<long double>; 

Helper.h

#ifndef HELPER_H_ 
#define HELPER_H_ 

#include <Base.h> 

class Helper { 
public: 
    Helper(Base* m); 
    virtual ~Helper(); 

}; 

#endif /* HELPER_H_ */ 

Helper.cpp

#include <Helper.h> 
#include <Base.h> 
#include <Derived.h> 

#include <iostream> 

using namespace std; 

Helper::Helper(Base* m) { 

    cout << "after received " << m << endl; 
    cout << "after fom: " << dynamic_cast< Derived<float>* >(m) << endl; 
    cout << "after dom: " << dynamic_cast< Derived<double>* >(m) << endl; 
    cout << "after ldom: " << dynamic_cast< Derived<long double>* >(m) << endl; 
    cout << "===" << endl; 
} 

Helper::~Helper() { 
} 

和使用该库可以是一个简单的代码:

TEST.CPP

#include <Derived.h> 
#include <Helper.h> 

#include <iostream> 

using namespace std; 

class MyModel : public Derived<double> { 
public: 
    MyModel() : Derived<double>() { 
    }; 

    virtual ~MyModel() { 
    };   

}; 

int main(int argc, char *argv[]) { 

    MyModel om1; 
    cout << "created mymodel " << &om1 << endl; 
    cout << "before fom: " << dynamic_cast< Derived<float>* >(&om1) << endl; 
    cout << "before dom: " << dynamic_cast< Derived<double>* >(&om1) << endl; 
    cout << "before ldom: " << dynamic_cast< Derived<long double>* >(&om1) << endl; 
    cout << "===" << endl; 
    Helper root(&om1); 

    return 0; 
} 

的问题是,当我创建共享库和链接test.cpp反对,该dynamic_cast失败。下面是一个例子输出:

created mymodel 0x7fff5fbff3e0 
before fom: 0 
before dom: 0x7fff5fbff3e0 
before ldom: 0 
=== 
after received 0x7fff5fbff3e0 
after fom: 0 
after dom: 0 // <<< Here I expected it to succeed and return a non-null pointer 
after ldom: 0 
=== 

但是,如果我编译整个库和实例一起,转换成功:

created mymodel 0x7fff5fbff3e0 
before fom: 0 
before dom: 0x7fff5fbff3e0 
before ldom: 0 
=== 
after received 0x7fff5fbff3e0 
after fom: 0 
after dom: 0x7fff5fbff3e0 
after ldom: 0 
=== 

我的问题是:为什么是dynamic_cast失败?

而且,在我想保持类似结构的前提下,继续使用共享库:我如何才能成功获得从Base*转换而来?

+0

关于缩小范围的建议...... 1)确保传递给你的编译器的选项对于你的lib组件和你的程序组件是一样的......它们会影响管理符号的形成。 2)使用'[nm](http://linux.about.com/library/cmd/blcmdl1_nm.htm)'工具从您的库对象和程序对象中转储符号...您应该看到相同的你的库组件(已定义)和你的测试代码(未定义)的损坏符号 – Andrew 2011-05-24 15:04:16

回答

3

我假设你在Linux/GCC上,因为在Windows上它应该“正常工作”。

它不会“与GCC一起工作”,因为对于性能RTTI支持GCC依赖于指针比较。这一切都在this GCC FAQ中解释,包括如何解决。编辑:虽然,这个常见问题表示它不能与dlopen()工作,而显式链接共享库应该工作;所以也许还有其他的东西,比如下面提到的错误。

我发现可以帮助其他一些环节:
dynamic_cast an interface from a shared library which was loaded by lt_dlopen(libtool) doesn't work
dynamic cast with interfaces
C++ dynamic_cast bug in Mac OS 10.6 Snow Leopard

+0

我在mac下使用gcc,我还没有尝试过Linux/gcc。我会看看你的链接。 – YuppieNetworking 2011-05-24 15:11:08

+0

昨天晚上我用Linux/GCC 4.4.5和4.5.2试了一下,它开箱即用。在Mac OSX/GCC 4.2.1上没有。要在Mac上解决这个问题,我遵循你的链接并使用'-mmacosx-version-min = 10.4'选项。奇怪的是,'-Wl,-no_compact_linkedit'标志对我不起作用,但我相信这可能是由CMake如何处理其编译器/链接器标志引起的。我仍然在寻找一个带gcc 4.2.1的旧* linux机器,以便测试。感谢您的帮助 – YuppieNetworking 2011-05-25 09:37:25

4

这里没有惊喜。即使对于正常的非模板类,您也不应该使用期望 RTTI跨共享库边界工作。对于一些编译器,在某些操作系统上,使用某些编译器或链接器选项,它可能会起作用,但通常它不会,也不需要(在标准中明确地将未指定为)。即使你做到了这一点,从长远来看也是不可持续的。

根据我的经验,RTTI无法在共享库边界之间交叉的情况远远超过了它可以的情况。

的解决方案是:

  • 限制衍生自这些类型的对象的所有的结构的共享库的代码,其中所使用的dynamic_cast的内(该溶液是相当困难的管理)。

  • 根本不要使用dynamic_cast(此解决方案很理想,很少适用)。

  • 不要使用共享库(评估共享库是否真的是你需要的,或者是从共享库中暴露更高级别的接口,而不暴露多态类型以便派生出来似乎表明“开放式架构”在您的应用程序中更合适))。

  • 定义自己的RTTI系统和转换操作符(这可能是困难的,这取决于你的技能,但它并不等于多的代码,很多主流项目中使用该解决方案,你可以找到大量的实例如何做到这一点)。

+0

您能举出一些定义他们自己的RTTI系统的主流项目的例子吗? – YuppieNetworking 2011-05-25 09:10:01

+1

LLVM,Boost.Serialization,大多数GUI工具,如Qt和VCL/CLX,DynObj和我自己的项目(这不是主流)。 – 2011-05-25 16:30:24

+0

谢谢Mikael。我正在认真考虑最后的选择......我必须首先检查一下我的枕头。 – YuppieNetworking 2011-05-26 16:20:54