web-dev-qa-db-ja.com

静的データをDLL

DLL静的なクラスを含むmembersがあります。このクラスのメソッドを使用するために__declspec(dllexport)を使用します。しかし、それを別のプロジェクトにリンクしてコンパイルしようとすると、静的データに対して「未解決の外部シンボル」エラーが発生します。

例えばDLLでは、Test.h

class __declspec(dllexport) Test{
protected:
    static int d;
public:
    static void m(){int x = a;}
}

DLLでは、Test.cpp

#include "Test.h"

int Test::d;

Testを使用するアプリケーションでは、m()を呼び出します。

また、メソッドごとに個別に__declspec(dllexport)を使用してみましたが、静的メンバーに対して同じリンクエラーが発生します。

Dumpbinを使用してDLL(.lib))を確認すると、シンボルがエクスポートされていることがわかります。

たとえば、アプリはリンク時に次のエラーを出します。

1>Main.obj : error LNK2001: unresolved external symbol "protected: static int CalcEngine::i_MatrixRow" (?i_MatrixRow@CalcEngine@@1HA)

しかし、.libのダンプビンには以下が含まれます。

Version      : 0
  Machine      : 14C (x86)
  TimeDateStamp: 4BA3611A Fri Mar 19 17:03:46 2010
  SizeOfData   : 0000002C
  DLL name     : CalcEngine.dll
  Symbol name  : ?i_MatrixRow@CalcEngine@@1HA (protected: static int CalcEngine::i_MatrixRow)
  Type         : data
  Name type    : name
  Hint         : 31
  Name         : ?i_MatrixRow@CalcEngine@@1HA

これを解決する方法がわかりません。何が悪いのですか?これらのエラーを回避するにはどうすればよいですか?

追伸コードは元々Linux用に開発され、.so/binaryの組み合わせは問題なく動作しました

編集:与えられたケースでは、静的変数はアプリケーションによって直接参照されませんが、メソッドはヘッダーにあるためインライン化されます。メソッドを.cppファイルに移動することで、リンクエラーを解決できました。

33
Gayan

Cprogramming.comの this スレッドでは、静的変数はdllに対してローカルであり、エクスポートされないことが推奨されます。

以下の議論の要約

静的メンバーは、DLLのクラスのメンバー関数を介してのみ、呼び出し側アプリケーションのコードから直接アクセスされません。ただし、静的メンバーにアクセスするinline関数がいくつかあります。これらの関数は、呼び出し側アプリケーションコードにインライン展開され、呼び出し側アプリケーションが静的メンバーに直接アクセスできるようにします。これは、静的変数がDLLに対してローカルであり、呼び出し元のアプリケーションから参照できないという、上記で参照された結果に違反します。

17
Anders Abel

私の推測では、DLLを使用するクラスは、ヘッダーにdllexportではなくdllimportが表示されるはずです。正しい場合、これは通常、次のようなプリプロセッサマクロを定義することで実現できます。

#ifdef EXPORTING
#define DECLSPEC __declspec(dllexport)
#else
#define DECLSPEC __declspec(dllimport)
#endif

そしてそれをクラス宣言で使用します:

class DECLSPEC Test{
protected:
    static int d;
public:
    static void m(){}
}

Test.cpp(またはDLLプロジェクト)で意味のある場所)では、-dllexportでエクスポートされるように、エクスポートすることを指定できます。

#define EXPORTING
#include "Test.h"

int Test::d;

一方、EXPORTINGを定義していない他のプロジェクトにはdllimportが表示されます。

それは意味がありますか?

11
Ghislain Fourny

Windows DLLでは、__declspec(dllexport)__declspec(dllimport)の間に特定の違いがあり、DLLのコンパイル時にはdllexportを使用し、次の場合にはdllimportを使用する必要があります。このDLLにリンクするプログラムをコンパイルします。これを定義する標準的な方法は、マクロを使用することです。

以下は、ビジュアルスタジオの例です。

// The following ifdef block is the standard way of creating macros which make exporting 
// from a DLL simpler. All files within this DLL are compiled with the DLL_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see 
// DLL_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
3