2012-10-22 38 views
4

我在Windows上遇到了CMake生成的DLL文件的混淆問題。在我的圖書館,我用奇異遞歸模板模式給予一定等級的唯一ID號:CMake生成的DLL和奇怪的循環模板的錯誤行爲(C++)

// da/Attribute.h: 

#ifndef DA_ATTRIBUTE_H 
#define DA_ATTRIBUTE_H 

namespace da { 

typedef unsigned int AttributeId; 

class AttributeBase { 
public: 
    virtual AttributeId getTypeId() const=0; 

protected: 
    /** Static ID counter. Every class that derives da::AttributeBase is 
     assigned an increment of this counter as its type ID number */ 
    static AttributeId sNextId; 

}; 

template <class Derived> 
class Attribute : public AttributeBase { 
private: 
    static AttributeId msTypeId; 

public: 
    Attribute() { 
     if (msTypeId == 0) { 
      msTypeId = ++sNextId; 
     } 
    } 

    virtual ~Attribute() { 

    } 

    /** For static contexts */ 
    static AttributeId typeId() { 
     if (msTypeId == 0) { 
      msTypeId = ++sNextId; 
     } 

     return msTypeId; 
    } 

    AttributeId getTypeId() const { 
     return typeId(); 
    } 

}; 

template <class Derived> AttributeId Attribute<Derived>::msTypeId = 0; 

} 

#endif 

問題是,當我的DLL鏈接到一個可執行的項目,似乎有一些不一致的地方用不同的ID方法。例如:

// Foo.h 
struct Foo : public da::Attribute<Foo> { 
    Foo() { } 
}; 

...

// main.cpp 
Foo *foo = new Foo;  

Foo->getTypeId() == 1 // True 
Foo::typeId() == 1 // Should be true, but isn't. Foo::typeId() == 2 

運行通過與GDB,在富:: getTypeID(),休息,我發現 「msTypeId」 和 「Foo :: msTypeId」有不同的內存地址。到底是什麼。

這隻發生在Foo被定義在DLL中時。 (並且只有在Windows 7中,顯然 - 我的Debian版本中沒有這個問題)如果我在main.cpp中創建派生類,或者只是將庫中的所有代碼編譯到可執行文件中,跳過DLL完全步驟,它沒有問題。

所有內容均使用MSYS和MinGW編譯,並在Windows 7 Home Premium上使用GCC 4.7編譯。

下面是庫中的CMakeLists.txt,萬一我搞砸了的東西有:

cmake_minimum_required(VERSION 2.6) 
project(foo) 

add_definitions(-std=c++0x) 
set(CMAKE_BUILD_TYPE Debug) 

set(sources 
    Foo.cpp 
) 

add_library(foo SHARED ${sources}) 

回答

2

你必須從共享庫導出類型。這是通過使用__declspec(dllexport) and __declspec(dllimport)修飾器完成的。閱讀MSDN文檔;這是相當複雜的。

由於頭需要具有__declspec(dllexport)構建庫和__declspec(dllimport)當編譯使用它的代碼的情況下,一個通常定義一個符號,習慣上稱爲LIBRARYNAME_EXPORT和它取決於LIBRARYNAME_EXPORTS是否被定義的#ifdef。

CMake在構建(共享)庫時自動定義target_EXPORTS。可以通過設置DEFINE_SYMBOL目標屬性來覆蓋它。

Unix選擇不同的路徑,默認情況下導出,並且還從共享庫中導入所有符號(除了靜態和明確隱藏的符號)。由於需要解決更多符號,這會導致一些性能損失,但它更易於使用(無需更改從靜態庫切換到共享庫),而且更靈活(即可以覆蓋共享庫中的符號你無法在Windows中完成)。