Visual Studio linker error when a template class matches __declspec (import)

It started with a seemingly minor problem that I encountered when I integrated my small exception handling library into a code base consisting of ~ 200 Visual C ++ projects in one Visual Studio solution.

I have a linker problem expressed by this message

3>B_Utils.lib(B_Utils.dll) : error LNK2005: "public: __cdecl ExceptionBase<class std::runtime_error>::ExceptionBase<class std::runtime_error>(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (??0?$ExceptionBase@Vruntime_error@std@@@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) already defined in TranslationUnit_2.obj
3>B_Utils.lib(B_Utils.dll) : error LNK2005: "public: virtual __cdecl ExceptionBase<class std::runtime_error>::~ExceptionBase<class std::runtime_error>(void)" (??1?$ExceptionBase@Vruntime_error@std@@@@UEAA@XZ) already defined in TranslationUnit_2.obj
3>B_Utils.lib(B_Utils.dll) : error LNK2005: "public: __cdecl ExceptionBase<class std::runtime_error>::ExceptionBase<class std::runtime_error>(class ExceptionBase<class std::runtime_error> const &)" (??0?$ExceptionBase@Vruntime_error@std@@@@QEAA@AEBV0@@Z) already defined in TranslationUnit_2.obj

At first glance, this looked like another typical problem, which can be solved with the help of a typical advice “try changing the order of #include files” or “deducing the implementation from the header file”, but this is not so.

I have studied a number of similar questions, such as this or this , but none of them suit my business. At least the suggested recipes do not help in my problem.

In addition, long-standing people in our company encountered another problem related to the Visual Studio linker during the migration to VS2010, which turned out to be a linker error, see here . In any case, none of them matched my scenario.

So, this tiny question ended with a whole mini-exploration. You can find details and an example of toys that reproduce the problem here on github . In the meantime, I will try to describe the situation below as briefly as possible.


The key components that cause the assembly to fail are:

  • 3 ( A, B, C) : B- > A, C- > B, C- > A.
  • A . A.
  • DLL- B, 2).
  • C , 2), 3).

:

A_SDK

(ExceptionBase.h)
template<typename T>
class ExceptionBase;
--
(foo.h)
#include "ExceptionBase.h"
inline void foo() // same effect would be with template<typename T> void foo()
{
    throw ExceptionBase<std::runtime_error>("Problem");
}

B_Utils

(Error.h)
#include "ExceptionBase.h"
#if defined(B_EXPORTS)
#define _B_UTILS_EXPORTS_CLASS      __declspec(dllexport)
#else
#define _B_UTILS_EXPORTS_CLASS      __declspec(dllimport)
#endif

struct _B_UTILS_EXPORTS_CLASS Error : public ExceptionBase<std::runtime_error>
{ Error(std::string&& s); } // ctor definition is in *.cpp file

C_Client

(TranslationUnit_1.cpp)
#include "A_SDK/foo.h"
#include "B_Utils/Error.h"      // !!! Comment this line --> Build succeeds
void TranslationUnit_1() { 
  foo();                        // !!! Comment this line --> Build succeeds
}
(TranslationUnit_2.cpp)
#include "A_SDK/foo.h"
void TranslationUnit_2() {
  foo();                        // !!! Comment this line --> Build succeeds
}

, // !!!. , . , github.

- , ? , :

  • ?
  • Visual Studio?

P.S. , . . github README. .

+4
2

Visual Studio , . , Visual Studio. , .


P.S.. , , ++- DLL ​​. , , .

, DLL, - (, COM), "hourglass". CppCon 2014, , .

+1

, . , , .

, , , , -

struct BaseWithVirtual
{
    virtual void what() {}       // !!! make this non-virtual --> Build succeeds
};

template<typename dummy>
struct ExceptionBase : public BaseWithVirtual
{
};

struct ExceptionChild : public  ExceptionBase<int>
{
};

inline void ThrowChild()
{
    ExceptionChild up;
    throw up;   // !!! comment this --> Build succeeds
}

ThrowChild.h dropbox, - < > .

, , .

+1

Source: https://habr.com/ru/post/1681575/


All Articles