2016-02-14 100 views
1

我正在寫返回指向基類的動態庫返回,但我想他們垂頭喪氣派生類。例如:溯造型類型從動態庫

//Library code: 
class A 
{ 
public: 
    A(void) = default; 
    virtual ~A(void) = default; 
    virtual void Foo(void) 
    { 
     std::cout << "A::Foo\n"; 
    }; 
}; 

class B : public A 
{ 
public: 
    B(void) = default; 
    void Foo(void) override 
    { 
     std::cout << "B::Foo\n"; 
    } 
    virtual void Bar(void) 
    { 
     std::cout << "B::Bar\n"; 
    } 
}; 

class Factory 
{ 
public: 
    Factory(void) = default; 
    virtual std::shared_ptr<A> CreateB(void) 
    { 
     return std::shared_ptr<A>{new B{}}; 
    } 
}; 

extern "C" std::shared_ptr<Factory> CreateFactory(void) 
{ 
    return std::make_shared<Factory>(); 
} 


//Application code: 
int main(int argc, char* argv[]) 
try 
{ 
    auto handle = dlopen("./Dynamic.so", RTLD_LAZY); 
    if (handle == nullptr) 
    { 
     throw std::runtime_error{dlerror()}; 
    } 
    auto factoryaddress = 
     reinterpret_cast<std::shared_ptr<Factory>(*)(void)>(
     dlsym(handle, "CreateFactory")); 
    if (factoryaddress == nullptr) 
    { 
     dlclose(handle); 
     throw std::runtime_error{dlerror()}; 
    } 
    auto factory = factoryaddress(); 
    auto a = factory->CreateB(); 
    a->Foo(); 
    auto b = std::dynamic_pointer_cast<B>(a); 
    b->Bar(); 
    std::cin.get(); 
} 

我得到

/usr/include/c++/5/bits/shared_ptr.h:458: undefined reference to `typeinfo for B' 
/usr/include/c++/5/bits/shared_ptr.h:458: undefined reference to `typeinfo for A' 

回答

1

我敢打賭,你表現出不同的代碼比你實際嘗試編譯。爲了有一個編譯時鏈接的問題,你必須

  1. 編譯RTTI支持,你做,因爲你用dynamic_cast
  2. 具有非內聯虛函數的類,它的RTTI信息你用。

在你的代碼一切都是在線的,但如果你拿,說~A(),放入單獨的源文件(不常見的標題),你會得到這個錯誤。

解決方法很簡單,但有些難看。所有導出的類都必須具有所有虛函數內聯。你可以有一個私有的非內聯非虛函數與實際的實現,只是從另一個調用。

所有這些背後的原因是,如果翻譯單元定義了類的第一個非內聯虛函數,則會發出RTTI信息(typeinfo和一些其他符號)。在你的情況下,當編譯應用程序時,編譯器不會看到任何虛函數定義,並假定typeinfo位於某個其他目標文件中。這是不正確的,因此鏈接器失敗。

當所有虛擬函數是內聯的,RTTI東西被髮射作爲弱在於指的是類的每個對象文件(也稱爲「COMDAT」一節)。所以你可以在庫和應用程序之間共享這個類,它們都有相同的RTTI符號。當應用程序加載庫時,會丟棄第二個重複的RTTI符號集,並將庫代碼鏈接到已存在於應用程序代碼中的符號。這是常見的弱符號力學。

+0

是的,在我的測試情況下,我把定義在一個單獨的文件。我會嘗試你的建議。 – Lyberta

+0

我可以在導出的類中使用純虛函數嗎? – Lyberta

+0

是的。純粹的虛擬功能就像主題內聯一樣行事。沒有純虛函數的二進制定義可用於確定哪個對象文件是該類的主要實現者。 –