如何将静态类实例正确导入DLL,跨平台?

时间:2012-11-27 17:04:36

标签: visual-c++ dll cmake

我有一个共享库(Linux中的.so,Windows中的.dll)需要访问包含在其加载的任何可执行文件中的静态变量。此变量恰好是类模板类型,并且在命名空间内。尽管将变量声明为“extern”(并且在Windows上,“__ declspec(dllimport)”),但当DLL链接时,VC10会为此变量提供“未解析的外部符号”错误。这对我来说似乎很奇怪,因为它确实应该被解决,而是留给加载时间。

标题:

// a header demonstrating MSVC-compatible linkage
#ifdef _MSC_VER

#ifdef I_AM_DLL
#define TO_DLL_LINKAGE __declspec( dllimport )
#else
#define TO_DLL_LINKAGE __declspec( dllexport )
#endif

#else  // not MSVC
#define TO_DLL_LINKAGE
#endif

template<class T>
class TheClass
{
public:
   TheClass(T t) : value_(t) {}

   T value() const
   {
      return value_;
   }
private:
   T value_;
};

typedef TheClass<int> MyClass;

和DLL:

// a test library (DLL) for linkage experiment
#define I_AM_DLL
#include "theclass.hpp"

#include <iostream>

namespace foo {
extern TO_DLL_LINKAGE MyClass theObject;
}

void bar() {
   int i = foo::theObject.value();
   std::cout << "object value is " << i << std::endl;
}

错误:

  

错误LNK2001:未解析的外部符号“_ declspec(dllimport)类TheClass foo :: theObject”( _imp_?theObject @ foo @@ 3V?$ TheClass @ H @@ A)

我想毫无疑问,这在gcc中运行良好。我还回顾了一些类似的StackOverflow问题,但是他们要么推荐我现在正在做的事情,要么出于各种原因不应用(例如导出而不是导入,类而不是类实例等)。 / p>

我需要什么额外的魔力让MSVC10开心?感谢。

1 个答案:

答案 0 :(得分:1)

事实证明这里存在两个基本问题:

  1. 在Windows上,符号解析在链接时执行,即使对于共享库也是如此
  2. dllimport和dllexport是不对称的 - 必须解决所有dllimports
  3. 在Linux下使用gcc运行时,我的程序可以工作,因为在程序加载时解析了对该对象的“extern”引用。共享库(.so)列出它正在导出的符号和正在导入的符号,OS的程序加载器验证在程序启动时是否满足主程序和共享库的所有导入。

    相比之下,在Windows / VC ++世界中,必须将标识为满足导入符号的特定模块,因为共享库已链接 - 通常通过“导入库”或.lib文件。这不能延迟到程序加载时间。因此,链接步骤失败。

    在我的特殊情况下,我有一个可执行模块,它们都需要来自共享库的符号,并提供(一个)符号。对于静态库和gcc / Linux,这不是问题,但对于Windows / VC ++,这会产生循环依赖。有一个解决方案,但它需要一些额外的努力,在this StackOverflow questionin the Microsoft documentation中讨论过。最重要的是,这将需要一个更复杂的链接步骤,其中从可执行文件生成导入库,以便在共享库的链接阶段使用。如果您有任何数据的 __ dllspec(dllexport)存储类,则会自动生成此类库。最后一步是将此导入库添加到共享库DLL的链接阶段。

    如果您是CMake用户,就像我一样,这个过程通过一个名为ENABLE_EXPORTS的特殊目标属性变得更加容易,它允许库“链接”到可执行文件。