Why is the linker not working when accessing a static member in an inline constructor

Just started doing some coding for a very simple automated test environment for internal use. (I know there are hundreds of them, really good ones too, but at the moment this is not interesting so don't point me to any of them please;)) Then I came across the following problem, t explain, thus asking for your help ...

I have the following code as part of a DLL:

(The code is barely an embryo, and it took me less than 2 minutes to write, so its logic, structure is nothing) has yet to be clarified.)

h file:

#pragma once

#ifdef __DLL__   // Defined in DLL-project
    #define DLLEXPORT __declspec( dllexport )
#else
    #define DLLEXPORT
#endif

class DLLEXPORT AutoTest
{
public:
            enum    eTestID {TESTID_SomeFunction};
                    AutoTest(eTestID id, LPVOID lpData)
                    {
                        if(sm_bTestsActive)
                            ExecTest(id, lpData);
                    }
            void    ActivateTests();

private:
    static  void    ExecTest(eTestID id, LPVOID lpData)
                    {
                    }
    static  BOOL    sm_bTestsActive;
};

      

CPP file:

#include "StdAfx.h"
#include "AutoTest.hpp"

BOOL AutoTest::sm_bTestsActive = FALSE;

void AutoTest::ActivateTests()
{
    sm_bTestsActive=TRUE;
}

      

This compiles just fine and the DLL gets built.

Here's my problem though - when creating a class with:

AutoTest(AutoTest::TESTID_SomeFunction, &SomeData);

      

from the main application, the linker crashes with

error LNK2001: unresolved external symbol "private: static int AutoTest::sm_bTestsActive" (?sm_bTestsActive@AutoTest@@0HA)

      

<2 minutes to write - now continuing 5 hours to understand why this is failing !!!: Oh

Here's what's interesting - if I translate the constructor into a cpp file (not nested) it works great!?!?

Here is the code:

h file:

#pragma once

#ifdef __DLL__   // Defined in DLL-project
    #define DLLEXPORT __declspec( dllexport )
#else
    #define DLLEXPORT
#endif

class DLLEXPORT AutoTest
{
public:
            enum    eTestID {FK3059};
                    AutoTest(eTestID id, LPVOID lpData);
            void    ActivateTests();

private:
    static  void    ExecTest(eTestID id, LPVOID lpData)
                    {
                    }
    static  BOOL    sm_bTestsActive;
};

      

CPP file:

#include "StdAfx.h"
#include "AutoTest.hpp"

BOOL AutoTest::sm_bTestsActive = FALSE;

AutoTest::AutoTest(eTestID id, LPVOID lpData)
{
    if(sm_bTestsActive)
        ExecTest(id, lpData);
}

void AutoTest::ActivateTests()
{
    sm_bTestsActive=TRUE;
}

      

(I made some minor changes to the code after pasting, so there may or may not be simple syntax errors.)

Also, if I remove the static member reference from the inline version constructor, it works fine.

Any ideas as to why the inline version won't work?

+3


source to share


2 answers


Should be:

#ifdef __DLL__   // Defined in DLL-project
    #define DLLEXPORT __declspec( dllexport )
#else
    #define DLLEXPORT __declspec( dllimport )
#endif

      



You can declare C ++ classes using the dllimport

or attribute dllexport

. These forms assume that the entire class is being imported or exported. Classes that are highlighted in this way are called exported classes.

More information in the documentation .

+1


source


Pay attention to your definition DLLEXPORT

.

When building the DLL, or __declspec(dllimport)

make sure it is properly expanded to __declspec(dllexport)

when you create the client.

I would suggest using a macro with a more specific name than the generic one DLLEXPORT

(to avoid conflicts with other macros with the same name).

Having static data members obtained from inline member functions works great for me (tested with VS2013).

Minimum playback:

Create a Visual Studio solution with an empty DLL project and an empty Console Application project.

Inside the DLL project, add two files:

DllClass.h

#pragma once

#ifndef TEST_DLL_CLASS
#define TEST_DLL_CLASS __declspec(dllimport)
#endif

class TEST_DLL_CLASS DllClass
{
public:
    DllClass();

    int GetMember() const
    {
        return m_data1;
    }

    static int GetStaticMember()
    {
        return sm_data2;
    }

private:
    int m_data1;
    static int sm_data2;
};

      

DllClass.cpp



#define TEST_DLL_CLASS __declspec(dllexport)
#include "DllClass.h"

int DllClass::sm_data2 = 2;

DllClass::DllClass()
    : m_data1(1)
{
}

      

Inside the console app project add one file:

test.cpp

#include "..\DllTestClass\DllClass.h"
#include <iostream>
using namespace std;

#pragma comment(lib, "DllTestClass")

int main()
{
    DllClass dllClass;

    cout << dllClass.GetMember() << endl;
    cout << DllClass::GetStaticMember() << endl;
}

      

Make sure that the linker can find the DLL.lib file (DllTestClass.lib) when you create the test console application.
To that end, you can navigate the project properties of the console app by navigating to:

Project Properties | Linker | Additional Library Directories

      

and add $(OutDir)

to additional library directories by doing this:

$(OutDir);%(AdditionalLibraryDirectories)

      

Builds and works right for me.

+2


source







All Articles