在Linux和Mac上开发项目一段时间后,终于有了一台装有Visual Studio 2015的Windows机器,我有两个通用的小库,一个叫做Platform,另一个叫Foundation,当我尝试链接Foundation时出现问题具有Platform的库(平台库已正确编译,没有特殊警告)输出以下链接器错误:
(我省略了其中一些)
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的以下头文件如下:
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 \设置为链接目录(使用cmake link_directories)
他们两个都在两个库中正确设置了__declspec(export)。
最佳答案
他们两个都正确设置了__declspec(export)
那是不适当的,因为它们都不都导出类。只有DLL可以。
您的Trace :: sm_level变量存储在DLL的数据部分中。对于驻留在同一模块中的代码,从同一模块读取和写入全局变量很简单,链接器知道该变量的地址。但是,如果另一个模块中的代码需要访问它,这并不简单。该地址不再可预测,该DLL可能已从其首选的基地址重定位。
需要额外的间接级别。一个指针,其目标地址固定为实际的DLL地址。编译器会自动进行处理,但是确实需要知道它不再是“正常”的全局变量。因此,它将使用指针,而不是尝试从其自己的数据部分读取指针。
链接器在抱怨什么,您的代码说它在您正在构建的同一模块中,#include这样做了。但是链接器在您正在构建的模块的数据部分中找不到Trace :: sm_level变量。当然,这是准确的,它存在于DLL中。
您可以通过声明类__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)。
关于c++ - 类中 protected 静态枚举的链接器错误,甚至已经定义,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33163225/