Static member used as default parameter leads to unresolved externals

2

I have a confusing issue using a static member variable as a default parameter. Since the same language construct works in a different place, it might be related to project (DLL) inter-dependencies. So please accept my apologies if my example is too complex, but I should draw the whole picture since I do not have any idea what is wrong.

I have a base class (representing kind of an error code)

ErrorBase.h

class ErrorBase
{
public:
    typedef unsigned long ErrorCode;

    /// here go the error codes. For reasons I do not want to explain, I cannot use an enumeration here.
    static const ErrorCode ERROR_UNINITIALIZED;
    static const ErrorCode ERROR_OK;
    ///...and so on

    ErrorBase(ErrorCode theCode = ERROR_UNINITIALIZED);
};

...and in ErrorBase.cpp, I am assigning values to the codes...

const ErrorBase::ErrorCode ErrorBase::ERROR_UNINITIALIZED = 0xffffffff;
const ErrorBase::ErrorCode ErrorBase::ERROR_OK = 0x0;
//.. and so on...

ErrorBase is exported from a DLL which provides some general purpose classes to our project

Now I am deriving another error class for more specific errors which has additional attributes specific for the particular type of error. The class SpecificError is part of a different DLL which links to the general purpose DLL containing ErrorBase. I have not included the dllimport/dllexport shebang, but we are using this all over the place and it works in all cases. If you have doubts, I can edit my code example.

SpecificError.h

class SpecificError : public ErrorBase
{
    public:
       static const ErrorCode SPECIFIC_ERROR_UNINITIALIZED;
       static const ErrorCode SPECIFIC_ERROR_SOMETHING_WENT_WRONG;

       SpecificError(ErrorCode theCode = SPECIFIC_ERROR_UNINITIALIZED);
};

... and in the SpecificError.cpp I am defining these values:

const SpecificError::ErrorCode SpecificError::SPECIFIC_ERROR_UNINITIALIZED = ErrorBase::ERROR_UNINITIALIZED;

Like ErrorBase, SpecificError is exported from the DLL handling specific functionality. Note that both error classes declare a constructor using the "UNINITIALIZED" value as a default for the error code.

Now I have a program being dependent on both DLLs, thus linking to both of them through the corresponding import libraries. This program includes ErrorBase.h and SpecificError.h. It does not seem to have any problems with ErrorCode.h, but about SpecificError.h I am receiving an

LNK2001 unresolved external symbol SpecificError::ErrorCode SpecificError::SPECIFIC_ERROR_UNINITIALIZED referenced in main.obj.

(remark: main.cpp does not explicitly use SpecificError, it just includes the header file).

I was able to work-around the problem by removing the default parameter from the SpecificError constructor and declaring a default constructor which in its implementation calls the inherited constructor of ErrorBase passing SPECIFIC_ERROR_UNINITIALIZED to it. This leads me to the assumption that the symbol SPECIFIC_ERROR_UNINITIALIZED is properly declared and defined but cannot be used as a parameter default. However, this seems to apply to SpecificError only, everything seems fine in ErrorBase.

Toolset: I am using Visual C++ 2017 as a compiler.

c++
asked on Stack Overflow Dec 12, 2019 by kritzel_sw • edited Dec 18, 2019 by kritzel_sw

3 Answers

6

I recreated the linked error. Make the following changes to your files and it should work just fine based on the code snippets that you showed above: SpecificError.cpp

// I sent theCode to the Base class
SpecificError::SpecificError(ErrorCode theCode) : ErrorBase(theCode)
{
// ...
}

In ErrorBase.cpp I just added the constructor but you probably already have this:

ErrorBase::ErrorBase(ErrorCode theCode) 
{ 
// ...
}

After I did this, I had to also move the initializations of the static consts to the .h from the .cpp files. Then I tested the code by doing:

    SpecificError e; // theCode ends up being 0xffffffff
    SpecificError e1(20); // theCode ends up being 20

I hope that this helps you.

Here is what my ErrorBase.cpp looks like:

#pragma once
#include"ErrorBase.h"
#include<iostream>

ErrorBase::ErrorBase(ErrorCode theCode) {
    std::cout << theCode << std::endl;
}

ErrorBase.h:

#pragma once
class ErrorBase
{
public:
    typedef unsigned long ErrorCode;

    static const ErrorCode ERROR_UNINITIALIZED = 0xffffffff;
    static const ErrorCode ERROR_OK = 0x0;

    ErrorBase(const ErrorCode = ERROR_UNINITIALIZED);
};

SpecificError.cpp:

#pragma once
#include"SpecificError.h" 

SpecificError::SpecificError(ErrorCode theCode) : ErrorBase(theCode)
{

}

SpecificError.h:

#pragma once
#include "ErrorBase.h"
class SpecificError : public ErrorBase
{
public:
    static const ErrorCode SPECIFIC_ERROR_UNINITIALIZED = ErrorBase::ERROR_UNINITIALIZED;
    static const ErrorCode SPECIFIC_ERROR_SOMETHING_WENT_WRONG = -42;

    SpecificError(ErrorCode theCode = SPECIFIC_ERROR_UNINITIALIZED);
};
answered on Stack Overflow Dec 12, 2019 by bhristov • edited Dec 20, 2019 by bhristov
3

I tried this and it is working, the class name was missing in ErrorBase.cpp

const ErrorBase::ErrorCode ErrorBase::ERROR_UNINITIALIZED = 0xffffffff;
const ErrorBase::ErrorCode ErrorBase::ERROR_OK = 0x0;

If still not working then let me know.

answered on Stack Overflow Dec 17, 2019 by Mannoj
1

You doing it wrong. The linker error means that it doesn't know where to get your constant value. You should use dynamic linkage with a first DLL. Let me show

Example of C++ class export: C++ How to export a static class member from a dll?

And your code should be changed:

ErrorBase.h

#ifndef MAIN_DLL
#define MAIN_DLL 1 // or you can add MAIN_DLL definition to the your first project Macroses
#endif

#if MAIN_DLL
#define ERROR_API  __declspec(dllexport) // export things to other modules
#else
#define ERROR_API __declspec(dllimport) // import things from the external DLL
#endif

class ERROR_API ErrorBase
{
public:
    typedef unsigned long ErrorCode;

    /// here go the error codes. For reasons I do not want to explain, I cannot use an enumeration here.
    static const ErrorCode ERROR_UNINITIALIZED;
    static const ErrorCode ERROR_OK;
    ///...and so on

    ErrorBase(ErrorCode theCode = ERROR_UNINITIALIZED);
};

SpecificError.h

#pragma once

#define MAIN_DLL 0
#include "../Dll_stack_ovfl1/ErrorBase.h" // change it to your path

class SpecificError : public ErrorBase
{
public:
    static const ErrorCode SPECIFIC_ERROR_UNINITIALIZED;
    static const ErrorCode SPECIFIC_ERROR_SOMETHING_WENT_WRONG;

    SpecificError(ErrorCode theCode = SPECIFIC_ERROR_UNINITIALIZED);
};

And the final step, configure the second DLL project to link it with exports of the first one:

Configuration Properties/Linker/Input/Additional dependencies

Add something like "$(SolutionDir)$(Configuration)\ErrorBase.lib"

Once again its just example, I don't know the real path of "lib" file and your project name for ErrorBase DLL - change it to your specific.

answered on Stack Overflow Dec 23, 2019 by progopis

User contributions licensed under CC BY-SA 3.0