来自 class 的受保护静态枚举的链接器错误,即使已经定义
Linker error of an protected static enum from a class, even being defined already
在 Linux 和 Mac 开发项目一段时间后,我终于得到了一台 Windows 机器 Visual Studio 2015,我有两个小库对于一般用途,一个称为 Platform,另一个称为 Foundation,当我尝试 link 带有 Platform 的 Foundation 库时出现问题(Platform 库已正确编译,没有特殊警告)输出以下 linker 错误:
(我省略了一些)
1>TUID.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>Units.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>Wildcard.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>RPC.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>SmartPtr.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>Stream.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>String.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>Natural.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>Numeric.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>Profile.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@@@1W4Type@TraceLevels@2@A)
平台库中 Trace.h 的以下 header 如下:
namespace TraceLevels
{
enum Type
{
Debug, ///< Debug logging messages.
Info, ///< General info messages.
Warning, ///< Warning messages.
Error, ///< Critical error messages.
};
}
typedef GameCore::TraceLevels::Type TraceLevel;
/// Trace interface.
class GAMECORE_PLATFORM_API Trace
{
public:
/// Default size for formatted trace message buffers without requiring dynamic memory allocation.
static const size_t DEFAULT_MESSAGE_BUFFER_SIZE = 1024;
/// @name Logging Interface
//@{
static void SetLevel( TraceLevel level );
static inline TraceLevel GetLevel();
static void Output( TraceLevel level, const char* pFormat, ... );
static void OutputVa( TraceLevel level, const char* pFormat, va_list argList );
//@}
protected:
/// Current logging level.
static TraceLevel sm_level; // This is the missing symbol which the linker complain
/// Logging level of the last message.
static TraceLevel sm_lastMessageLevel;
/// True if logging just started a fresh line.
static bool sm_bNewLine;
/// @name Logging Implementation
//@{
static void OutputImplementation( const char* pMessage );
//@}
/// @name Static Utility Functions
//@{
static const char* GetLevelString( TraceLevel level );
//@}
};
并将它们的定义正确添加到源文件中 (Trace.cpp)
using namespace GameCore;
GameCore::TraceLevel Trace::sm_level = GameCore::TraceLevels::Info;
GameCore::TraceLevel Trace::sm_lastMessageLevel = GameCore::TraceLevels::Debug;
bool GameCore::Trace::sm_bNewLine = true;
我正在使用 CMake 生成我的解决方案文件,输出的 .lib 和 .dll 位于 lib\ 和 bin\ 目录中,在根构建目录(源代码之外)内并设置 lib\作为 link 目录(使用 cmake link_directories)
他们都在两个库中正确设置了 __declspec(导出)。
Both they have set the __declspec(export) properly
这是不对的,它们不都导出 class。只有 DLL 可以。
您的 Trace::sm_level 变量存储在 DLL 的数据部分。对于同一模块中的代码,从同一模块读写全局变量很简单,链接器知道变量的地址。但如果另一个模块中的代码需要访问它,那就不简单了。地址不再可预测,DLL 可能已从其首选基地址重新定位。
需要额外的间接级别。目标地址固定为实际 DLL 地址的指针。编译器会自动处理这个问题,但它确实需要知道这不再是一个"normal"全局变量。所以它将使用指针而不是尝试从它自己的数据部分读取它。
链接器抱怨的是什么,您的代码说它在您正在构建的同一个模块中,#include 就是这样做的。但是链接器无法在您正在构建的模块的数据部分中找到 Trace::sm_level 变量。这当然是准确的,它存在于 DLL 中。
您通过声明 class __declspec(dllimport) 告诉编译器这一点。现在知道了。
我们看不到GAMECORE_PLATFORM_API是什么样子,但问题出在通心粉上。它应该是这样的:
#if BUILDING_GAMECORE
# define GAMECORE_PLATFORM_API __declspec(dllexport)
#else
# define GAMECORE_PLATFORM_API __declspec(dllimport)
#endif
并更改构建 DLL 的项目,使用项目 > 属性 > C/C++ > 预处理器 > 预处理器定义,添加 BUILDING_GAMECORE。在任何使用 DLL 的项目中无需进行任何更改,他们现在将看到 __declspec(dllimport).
在 Linux 和 Mac 开发项目一段时间后,我终于得到了一台 Windows 机器 Visual Studio 2015,我有两个小库对于一般用途,一个称为 Platform,另一个称为 Foundation,当我尝试 link 带有 Platform 的 Foundation 库时出现问题(Platform 库已正确编译,没有特殊警告)输出以下 linker 错误:
(我省略了一些)
1>TUID.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>Units.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>Wildcard.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>RPC.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>SmartPtr.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>Stream.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>String.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>Natural.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>Numeric.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@1W4Type@TraceLevels@2@A)
1>Profile.obj : error LNK2001: unresolved external symbol "protected: static enum GameCore::TraceLevels::Type GameCore::Trace::sm_level" (?sm_level@Trace@GameCore@@@@1W4Type@TraceLevels@2@A)
平台库中 Trace.h 的以下 header 如下:
namespace TraceLevels
{
enum Type
{
Debug, ///< Debug logging messages.
Info, ///< General info messages.
Warning, ///< Warning messages.
Error, ///< Critical error messages.
};
}
typedef GameCore::TraceLevels::Type TraceLevel;
/// Trace interface.
class GAMECORE_PLATFORM_API Trace
{
public:
/// Default size for formatted trace message buffers without requiring dynamic memory allocation.
static const size_t DEFAULT_MESSAGE_BUFFER_SIZE = 1024;
/// @name Logging Interface
//@{
static void SetLevel( TraceLevel level );
static inline TraceLevel GetLevel();
static void Output( TraceLevel level, const char* pFormat, ... );
static void OutputVa( TraceLevel level, const char* pFormat, va_list argList );
//@}
protected:
/// Current logging level.
static TraceLevel sm_level; // This is the missing symbol which the linker complain
/// Logging level of the last message.
static TraceLevel sm_lastMessageLevel;
/// True if logging just started a fresh line.
static bool sm_bNewLine;
/// @name Logging Implementation
//@{
static void OutputImplementation( const char* pMessage );
//@}
/// @name Static Utility Functions
//@{
static const char* GetLevelString( TraceLevel level );
//@}
};
并将它们的定义正确添加到源文件中 (Trace.cpp)
using namespace GameCore;
GameCore::TraceLevel Trace::sm_level = GameCore::TraceLevels::Info;
GameCore::TraceLevel Trace::sm_lastMessageLevel = GameCore::TraceLevels::Debug;
bool GameCore::Trace::sm_bNewLine = true;
我正在使用 CMake 生成我的解决方案文件,输出的 .lib 和 .dll 位于 lib\ 和 bin\ 目录中,在根构建目录(源代码之外)内并设置 lib\作为 link 目录(使用 cmake link_directories)
他们都在两个库中正确设置了 __declspec(导出)。
Both they have set the __declspec(export) properly
这是不对的,它们不都导出 class。只有 DLL 可以。
您的 Trace::sm_level 变量存储在 DLL 的数据部分。对于同一模块中的代码,从同一模块读写全局变量很简单,链接器知道变量的地址。但如果另一个模块中的代码需要访问它,那就不简单了。地址不再可预测,DLL 可能已从其首选基地址重新定位。
需要额外的间接级别。目标地址固定为实际 DLL 地址的指针。编译器会自动处理这个问题,但它确实需要知道这不再是一个"normal"全局变量。所以它将使用指针而不是尝试从它自己的数据部分读取它。
链接器抱怨的是什么,您的代码说它在您正在构建的同一个模块中,#include 就是这样做的。但是链接器无法在您正在构建的模块的数据部分中找到 Trace::sm_level 变量。这当然是准确的,它存在于 DLL 中。
您通过声明 class __declspec(dllimport) 告诉编译器这一点。现在知道了。
我们看不到GAMECORE_PLATFORM_API是什么样子,但问题出在通心粉上。它应该是这样的:
#if BUILDING_GAMECORE
# define GAMECORE_PLATFORM_API __declspec(dllexport)
#else
# define GAMECORE_PLATFORM_API __declspec(dllimport)
#endif
并更改构建 DLL 的项目,使用项目 > 属性 > C/C++ > 预处理器 > 预处理器定义,添加 BUILDING_GAMECORE。在任何使用 DLL 的项目中无需进行任何更改,他们现在将看到 __declspec(dllimport).