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ファイルに移動することで、リンクエラーを解決できました。
Cprogramming.comの this スレッドでは、静的変数はdllに対してローカルであり、エクスポートされないことが推奨されます。
静的メンバーは、DLLのクラスのメンバー関数を介してのみ、呼び出し側アプリケーションのコードから直接アクセスされません。ただし、静的メンバーにアクセスするinline関数がいくつかあります。これらの関数は、呼び出し側アプリケーションコードにインライン展開され、呼び出し側アプリケーションが静的メンバーに直接アクセスできるようにします。これは、静的変数がDLLに対してローカルであり、呼び出し元のアプリケーションから参照できないという、上記で参照された結果に違反します。
私の推測では、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が表示されます。
それは意味がありますか?
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