2011-05-24 144 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