mrzli
mrzli

Reputation: 17369

Unresolved external symbol static variable (variable used by method defined in header)

Here is the .h:

class Logger
{
private:
    static int mTresholdSeverity;

public:
    static __declspec(dllexport) void log(const char* message);
    static __declspec(dllexport) void logFormat(const char* format, ...);

    static __declspec(dllexport) int getTresholdSeverity() { return mTresholdSeverity; }
    static __declspec(dllexport) void setTresholdSeverity(int tresholdSeverity) { mTresholdSeverity = tresholdSeverity; }
};

And .cpp:

#include "Logger.h"
#include <cstdarg>

int Logger::mTresholdSeverity = 200;

void Logger::log(const char* message)
{
    //...
}

void Logger::logFormat(const char* format, ...)
{
    //...
}

I get this error:
error LNK2001: unresolved external symbol "private: static int TransformationViewer_Utility_Logging::Logger::mTresholdSeverity" (?mTresholdSeverity@Logger@TransformationViewer_Utility_Logging@@0HA) ...

Obviously, mTresholdSeverity is initialized. The error is removed if I comment out getTresholdSeverity() and setTresholdSeverity() or if I move their definition into .cpp file.

Why is there a link error when a static method defined in header file (getTresholdSeverity() or setTresholdSeverity()) uses a static variable (mTresholdSeverity)?

Upvotes: 4

Views: 4576

Answers (3)

Pete Becker
Pete Becker

Reputation: 76345

Just so this doesn't get lost in the noise: you can change your accessors into ordinary, non-inline functions and define them in the same source file as your static data member. As long as you export them, you'll be able to call them from anywhere, and they'll access the static data just fine.

Upvotes: 0

Pavel Radzivilovsky
Pavel Radzivilovsky

Reputation: 19114

Here is how it works.

Every DLL (or EXE) or otherwise complete "fully-linked" binary, must have definitions of all referenced names, including static variables, including static data members in C++ classes.

They will be separate across DLLs in the application. That means, this variable value will be different, depending on which DLL you look from. Moving the functions to CPP file will make them do a different thing: they will now see the DLL's copy of the variable and not the EXE's.

To make what you wrote compile, there has to be the definition from the CPP present in all user binaries at one place. That means, the DLL and the users of the DLL (EXEs or DLLs) must have one CPP file with this definition.

This is a very big hassle, because among other things it makes Singleton pattern impossible (having a shared data object for all users within the program) and each copy of a DLL must have its own static state. Such problem exists only on Windows, with DLLs which are dynamic *loaded * libraries. On UNIX systems, there is a different technology known as Shared Objects (SO). They support true dynamic linking, which means, running the linker to resolve external names at runtime.

Upvotes: 5

Pete Becker
Pete Becker

Reputation: 76345

The problem is that mThresholdSeverity is not exported from the DLL, but the two accessors are defined inline, so wherever they're called they have to be able to see mThresholdSeverity. There are two solutions: either export mThresholdSeverity from the DLL (sorry, I don't remember how to do that off the top of my head), or make the accessors non-inline functions, define them in the DLL, and export them from the DLL.

Upvotes: 0

Related Questions