我在Windows上遇到了CMake生成的DLL文件的一个令人困惑的问题。在我的库中,我使用Curiously Recurring Template Pattern为某些类提供唯一的ID号:
// 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运行,在Foo :: getTypeID()中断,我发现&#34; msTypeId&#34;和&#34; Foo :: msTypeId&#34;有不同的记忆地址。到底是什么。
这只会在DLL中定义Foo时发生。 (仅在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})
答案 0 :(得分:2)
您必须从共享库中导出类型。这是使用__declspec(dllexport)
and __declspec(dllimport)
装饰器完成的。阅读MSDN文档;它很参与。
由于标头在构建库时需要__declspec(dllexport)
,而在编译使用它的代码时需要__declspec(dllimport)
,因此通常会定义一个符号,通常称为LIBRARYNAME_EXPORT
和#ifdefs取决于它是否定义LIBRARYNAME_EXPORTS
。
CMake在构建(共享)库时自动定义target_EXPORTS
。可以通过设置DEFINE_SYMBOL
目标属性来覆盖它。
Unix选择不同的路径,默认情况下导出并导入共享库中的所有符号(静态和显式隐藏的除外)。这会导致一点性能损失,因为需要解析更多符号,但它更容易使用(从静态切换到共享库不需要更改)并且更加灵活(即可以覆盖共享库中的符号,你不能在Windows中做。)