3

I am creating a DLL in C++ using Visual Studio 2013 on Windows 8.1 Update 1. There is a class called XMLData which has a public member function called getAttribute.

XMLData.h

namespace DDGL
{
    class DLL_EXPORTED XMLData
    {

       ...

       // const char* is used to avoid problems with trying to pass an
       // std::string over DLL boundaries
       template <typename Type> Type getAttribute (const char* attribute) const;

       ...

    };
}

Inside the DLL, every use is instantiated as you'd expect and works fine.

However, inside an application I will of course get undefined references for the <typename Type>'s that haven't been used within the DLL.

So, I try to use explicit template instantiation (I'd rather not put the implementation in header, for a learning exercise if anything):

XMLData.cpp

namespace DDGL
{

    ...

    // getAttribute definition

    ...

    template float XMLData::getAttribute(const char* attribute) const;

    ...

}

However, I still get an unresolved external in the application using the DLL:

Output

error LNK2019: unresolved external symbol "public: float __thiscall DDGL::XMLData::getAttribute<float>(char const *)const " (??$getAttribute@M@XMLData@DDGL@@QBEMPBD@Z) referenced in function "class std::shared_ptr<class DDGL::IGameObject> __cdecl SimpleExplodingRocketDeserialiser(class DDGL::XMLData)" (?SimpleExplodingRocketDeserialiser@@YA?AV?$shared_ptr@VIGameObject@DDGL@@@std@@VXMLData@DDGL@@@Z)

DLL_EXPORTED

#ifdef DDGL_DLL_BUILDING
    #define DLL_EXPORTED __declspec(dllexport)
#else
    #define DLL_EXPORTED __declspec(dllimport)
#endif

Where am I going wrong?

OMGtechy
  • 7,935
  • 8
  • 48
  • 83
  • An explicit instantiation could help you only if you use static linking (via *.lib files), not dynamic one (*.dll). – Constructor May 10 '14 at 14:28
  • @Constructor Why on earth not? – OMGtechy May 10 '14 at 14:43
  • Because templates is a compiler-time feature, not a run-time one. – Constructor May 10 '14 at 14:45
  • @Constructor and DLLs are compiled? – OMGtechy May 10 '14 at 14:46
  • Yes, but they are loaded at run time. You are trying to compile the main program without linking to it the code of your template instantiations. – Constructor May 10 '14 at 14:49
  • @Constructor Aren't they linked like any other function in a DLL? – OMGtechy May 10 '14 at 14:50
  • Yes, but the problem in the main program, isn't it? – Constructor May 10 '14 at 14:51
  • 1
    @Constructor My point is, the purpose of explicit template instantiation is so things like this can be done. It is a compile time feature. I instantiate the required template in the DLL, but it cannot link against it in the application the uses the DLL. – OMGtechy May 10 '14 at 15:05
  • I'm sorry I inattentively read your question. There is a syntax in Visual C++ to export template instantiations from a DLL. But you doesn't use it properly. I can't find a good article on this task in MSDN right now, but here is one which is close enough and can help you: ["How to export an instantiation of a Standard Template Library (STL) class and a class that contains a data member that is an STL object"](http://support.microsoft.com/kb/168958/en-us). It is about STL template classes but your case is similar to it. – Constructor May 10 '14 at 15:34
  • @Constructor thank you :) So, looking at that article, it seems like I am not exporting the symbol for the instantiations, even though it is indeed being compiled within the DLL. – OMGtechy May 10 '14 at 15:36

2 Answers2

2

Although @OMGtechy solution works, I think it's an overkill. It's sufficient to specify DLL_EXPORTED for both the template declaration and template definition.

In the header file:

namespace DDGL
{
    class DLL_EXPORTED XMLData
    {
       template <typename Type> DLL_EXPORTED Type getAttribute (const char* attribute) const;

    };
}

In the .cpp file defining the function:

namespace DDGL
{
    template<> DLL_EXPORTED float XMLData::getAttribute(const char* attribute) const {
        // Function body goes here
    }
}

Tested on MSVC++2017 .

Some thoughts: I think it would be logical if DLL_EXPORTED on class definition worked on its template methods too. Non-templated methods do not require DLL_EXPORTED again on each method. So what's so different for template methods? I don't know for sure.

Serge Rogatch
  • 13,865
  • 7
  • 86
  • 158
1

The problem was that, whilst I was indeed explicitly instantiating the template correctly, I was not exporting the symbol for each instantiation.

The solution was to do the following:

namespace DDGL
{
    class DLL_EXPORTED XMLData
    {

       ...

       // const char* is used to avoid problems with trying to pass an
       // std::string over DLL boundaries
       template <typename Type> Type getAttribute (const char* attribute) const;

       ...

    };

    DLL_TEMPLATE_CLASS_MEMBER float XMLData::getAttribute(const char* attribute) const;
}

Where DLL_TEMPLATE_CLASS_MEMBER was defined as:

#ifdef DDGL_DLL_BUILDING
    #define DLL_TEMPLATE_CLASS_MEMBER template DLL_EXPORTED
#else
    #define DLL_TEMPLATE_CLASS_MEMBER extern template DLL_EXPORTED
#endif

Doing this correctly exported the symbols for the explicit template instantiation and allowed me to use them outside the DLL.

OMGtechy
  • 7,935
  • 8
  • 48
  • 83