首页 文章

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

提问于
浏览
1

我有一个共享库(Linux中的.so,Windows中的.dll)需要访问包含在其加载的任何可执行文件中的静态变量 . 此变量恰好是类模板类型,并且在命名空间内 . 尽管将变量声明为"extern"(并且在Windows上,"__declspec(dllimport)"),VC10会为此变量 when the DLL is linked 提供"unresolved external symbol"错误 . 这对我来说似乎很奇怪,因为它确实无法解决,而是留给加载时间 .

Headers :

// 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问题,但是他们要么推荐我已经在做的事情,要么出于各种原因不应用(例如导出而不是导入,类而不是类实例等) .

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

1 回答

  • 1

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

    • 在Windows上,符号解析在链接时执行,即使对于共享库也是如此

    • dllimport和dllexport是不对称的 - 必须解决所有dllimports

    在Linux下使用gcc运行时,我的程序可以工作,因为在程序加载时解析了对该对象的“extern”引用 . 共享库(.so)列出它正在导出的符号和导入的符号,OS的程序加载器验证在启动程序时是否满足主程序和共享库的所有导入 .

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

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

    如果您是CMake用户,就像我一样,这个过程通过一个名为ENABLE_EXPORTS的特殊目标属性变得更加容易,该属性允许库为"link to"可执行文件 .

相关问题