不一致的dll链接&不允许定义dllimport静态数据成员

时间:2013-11-12 10:32:40

标签: visual-c++ dllimport dllexport

假设我有这两个文件:

Header.h

class DLL ExportClass{
public:
  ExportClass();
  static int test;
};

Source.cpp

#ifdef EXPORT
    #define DLL __declspec(dllexport)
#else
    #define DLL __declspec(dllimport)
#endif

#include "Header.h"

int ExportClass::test = 0;
ExportClass::ExportClass(){
}

我不会定义EXPORT(导入已导出的具有static成员的类),为什么我会收到这些警告:

1>source.cpp(11): warning C4273: 'test' : inconsistent dll linkage
1>          header.h(4) : see previous definition of 'public: static int ExportClass::test'
1>source.cpp(13): warning C4273: 'ExportClass::ExportClass' : inconsistent dll linkage
1>          header.h(3) : see previous definition of '{ctor}'

这个错误:

1>source.cpp(11): error C2491: 'ExportClass::test' : definition of dllimport static data member not allowed

如果我定义EXPORT它有效。我有点理解这些警告,但我想,编译器可以忽略静态变量和ctor,因为无论如何整个类都被声明为__declspec(dllimport)。我想为__declspec(dllexport)__declspec(dllimport)使用相同的代码库 - 但似乎编译器stll尝试在其声明中定义标记为__declspec(dllexport)的这些符号。解决这个问题的常见做法是什么?

3 个答案:

答案 0 :(得分:19)

您期望编译器忽略非常严重的事故。它在类声明中遇到了__declspec(dllimport)属性,它非常明确地声明类实现存在于将在运行时绑定的不同模块中。但是它也遇到了这个定义,完全出乎意料,因为属性契约说它是在一个完全不同的项目中编译的。

生成C4273警告是为了提醒您,在运行时实际要执行的功能是非常不清楚的。有两个,一个忙于编译,另一个在DLL中。哪一个实际执行是一个疯狂的猜测。 C4273是1级警告,适合“这几乎肯定是错误的”类别。工作并不是完全不可能的,因为某些期望函数至少具有相同的代码。然而,不会造成麻烦的几率并不大,它只有在函数没有任何改变内部DLL状态的副作用时才能工作。当它发生错误时很难诊断出来。

然后它遇到了导出的变量。同样的情况,有两个。这是编译器程序员放下脚本的地方,让代码随机使用一个或另一个不再是可以忽略的东西。那就是无法工作,变量不能具有相同的价值。所以C2491是一个很难的错误。

不知道你是怎么进入这个泡菜的,显然你想要旅行的路会让你从陡峭的悬崖上掉下来。

答案 1 :(得分:3)

我可以重现您的问题的唯一方法是执行以下操作:

  1. 创建一个Win32 DLL项目,将其命名为 Project1
  2. 按照描述添加源代码
  3. 编译DLL和LIB
  4. 更改项目属性以从预处理器定义中删除EXPORT
  5. 再次尝试编译(然后我会看到您的错误/警告)
  6. 如果我执行以下操作而不是步骤4和5,则不会看到错误:

    创建一个Win32控制台应用程序,将其命名为 Project2

    添加源代码如下:

    #include "Project1.h"
    
    #pragma comment(lib, "Project1.lib")
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        ExportClass pClass;
        return 0;
    }
    

    我怀疑你看到了这些错误,因为你正在从同一个DLL项目中做所有事情,并且它覆盖了之前创建的LIB,然后尝试导入它。

    如果我在猜测你做了什么是正确的,你可以尝试使用另一个项目的DLL / LIB,看看会发生什么?

答案 2 :(得分:0)

尽管这是一个旧线程,但其他人可能会阅读它。因此,如果您想使该代码可交叉编译,我通常会定义一个标头“ export.h”,例如:

export.h

#pragram once

#if ! defined(DLL_API)
#   if defined(_WIN32)  // for windows builds
#       if defined(myDLL_EXPORTS)
#           define DLL_API __declspec(dllexport)
#       else
#           define DLL_API __declspec(dllimport)
#       endif
#   else               // for linux builds
#       define DLL_API
#   endif
#endif

,并将其包含在要从dll导出的所有类(.h)中。您还必须将变量myDLL_EXPORTS定义为dll项目的编译器的参数。

当编译动态库(dll / so)时,它的工作方式非常简单,因为定义了变量myDLL_EXPORTS,所以编译器将__declspec(dllexport)替换DLL_API,以便您的类可以被您的dll用户。相反,当包含要在其上使用类的头文件时,由于变量myDLL_EXPORTS未在使用者项目中定义(仅在DLL项目中定义),编译器将用__declspec(dllimport)替换myDLL_EXPORT。 ,因此它知道您的类符号是在其他地方定义的(在这种情况下,是在dll / so中定义的)。

最后,因为__declspec(...)是仅限Windows的东西,对于Linux,我们什么都没有替换DLL_API。