Reputation: 167
After a little a while developing a project in Linux and Mac, finally I got a Windows machine with Visual Studio 2015, I have an two little libraries for general purpose one called Platform and another called Foundation, the problem when I try to link the Foundation library with Platform (which the Platform library was compiled correctly with no special warnings) outputs the following linker errors:
(I've omitted some of those)
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)
The following header of Trace.h from the Platform library is the following:
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 );
//@}
};
And their definitions properly added in their source file (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;
I'm using CMake to generate my solution files, and the output .lib and .dll are located in lib\ and bin\ directories, inside of the root build directory (out of source) and set the lib\ as an link directory (using cmake link_directories)
Both they have set the __declspec(export) properly set in both libraries.
Upvotes: 0
Views: 1052
Reputation: 941327
Both they have set the __declspec(export) properly
That is not proper, they don't both export the class. Only the DLL does.
Your Trace::sm_level variable is stored in the data section of the DLL. Reading and writing global variables from the same module is simple for code that lives in the same module, the linker knows the address of the variable. But not simple if code in another module needs to access it. The address is no longer predictable, the DLL might have been relocated from its preferred base address.
An extra level of indirection is required. A pointer whose target address is fixed-up for the actual DLL address. The compiler will take care of this automagically, but it does need to know that this is not a "normal" global variable anymore. So it will use the pointer instead of trying to read it from its own data section.
Which is what the linker is complaining about, your code says that it is in the same module you are building, the #include did so. But the linker cannot find the Trace::sm_level variable in the data section of the module you are building. Which is accurate of course, it lives inside the DLL.
You tell the compiler about this by declaring the class __declspec(dllimport). Now it knows.
We can't see what GAMECORE_PLATFORM_API looks like, but it is the macaroni with the problem. It should look like this:
#if BUILDING_GAMECORE
# define GAMECORE_PLATFORM_API __declspec(dllexport)
#else
# define GAMECORE_PLATFORM_API __declspec(dllimport)
#endif
And change the project that builds the DLL, use Project > Properties > C/C++ > Preprocessor > Preprocessor Definitions, add BUILDING_GAMECORE. No changes necessary in any projects that uses the DLL, they will now see __declspec(dllimport).
Upvotes: 1