受保护的静态枚举与类的链接器错误,甚至已经定义

时间:2015-10-16 05:35:56

标签: c++ enums linker

经过一段时间在Linux和Mac上开发一个项目,最后我得到了一台带有Visual Studio 2015的Windows机器,我有两个通用的小库,一个名为Platform,另一个名为Foundation,当我尝试将Foundation库与Platform(已正确编译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)

Platform库中的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(导出)。

1 个答案:

答案 0 :(得分:1)

  

他们都正确设置了__declspec(导出)

这不合适,他们都没有出口课程。只有DLL可以。

您的Trace :: sm_level变量存储在DLL的数据部分中。对于存在于同一模块中的代码,从同一模块读取和写入全局变量很简单,链接器知道变量的地址。但如果另一个模块中的代码需要访问它,那就不简单了。该地址不再可预测,DLL可能已从其首选基址重新定位。

需要额外的间接级别。一个指针,其目标地址固定为实际的DLL地址。编译器会自动处理这个问题,但 需要知道这不是&#34;正常&#34;全局变量。因此它将使用指针而不是尝试从其自己的数据部分读取它。

链接器抱怨的是什么,你的代码说它与你正在构建的模块相同,#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的项目,使用Project&gt;属性&gt; C / C ++&gt;预处理器&gt;预处理器定义,添加BUILDING_GAMECORE。在使用DLL的任何项目中都不需要进行任何更改,现在它们将看到__declspec(dllimport)。