Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When exporting STL std::basic_string template from DLL, I get a LNK2005 error

OK, so I've read several question and articles about this topic, and I feel like I understand the basics, but I'm still having trouble.

I have a DLL that exports a class that has a std::string as a member. My main program contains classes that also have strings, and it uses the DLL.

If I compile the DLL in VS2010, I get the following warnings:

warning C4251: 'MyClass::data' : class 'std::basic_string<_Elem,_Traits,_Ax>' needs to have dll-interface to be used by clients of class 'MyClass'

When I compile the EXE, I get the same warnings, but there are no errors, and the program compiles and runs. In reality, it's a large project, so I get like 40 warnings, and I'm not too keen on that. (As a side-observation, these warnings are not present when compiled with VS2008)

So, I read about that warning, and it lead me to this MS article: http://support.microsoft.com/default.aspx?scid=KB;EN-US;168958 which tells how to export a STL template from a DLL to satisfy the warnings I was getting.

The problem is, when I add the following lines to remove the warnings:

EXPIMP_TEMPLATE template class DECLSPECIFIER std::allocator<char>;
EXPIMP_TEMPLATE template class DECLSPECIFIER std::basic_string< char, std::char_traits<char>, std::allocator<char> >;

the DLL compiles with no warnings, but when I compile my EXE, the linker throws a fit:

2>SampleDLL.lib(SampleDLL.dll) : error LNK2005: "public: __thiscall std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >::~basic_string<char,struct std::char_traits<char>,class std::allocator<char> >(void)" (??1?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QAE@XZ) already defined in OtherClass.obj
2>SampleDLL.lib(SampleDLL.dll) : error LNK2005: "public: unsigned int __thiscall std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >::size(void)const " (?size@?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@QBEIXZ) already defined in OtherClass.obj

Both the DLL and the EXE are compiled with the same code generation options. I can use MT on both or MD, and the results are the same.

I am including the code from a minimized sample program in case I left anything out above.

My main question: Can I fix the LNK2005 errors, or is it safe to just ignore the C4251 warnings?

Edit: So I've read a little more, and it looks like if the std::string that the DLL class uses is a private variable that is only accessed by member functions, it may be safe to ignore the warning... Any comments on this? Is this a step in the right direction?

DLL code:

#pragma once

#include <exception>
#include <string>


#ifdef SAMPLEDLL_EXPORTS
#    define DECLSPECIFIER __declspec(dllexport)
#    define EXPIMP_TEMPLATE
#else
#    define DECLSPECIFIER __declspec(dllimport)
#    define EXPIMP_TEMPLATE extern
#endif

//disable warnings on extern before template instantiation (per MS KB article)
#pragma warning (disable : 4231)
//std::basic_string depends on this allocator, so it must also be exported.
EXPIMP_TEMPLATE template class DECLSPECIFIER std::allocator<char>;
//std::string is a typedef, so you cannot export it.  You must export std::basic_string
EXPIMP_TEMPLATE template class DECLSPECIFIER std::basic_string< char, std::char_traits<char>, std::allocator<char> >;
#pragma warning (default : 4231)

class DECLSPECIFIER MyClass
{
public:
    std::string getData(); //returns 'data', body in CPP file
private:
    std::string data;
    int data2;
};

//in SampleDLL.cpp file...
std::string MyClass::getData() { return data; }

EXE code:

#include <iostream>
#include "SampleDLL.h"

using namespace std;

void main()
{
    MyClass class1;

    cout << class1.getData() << endl;

}
like image 327
JPhi1618 Avatar asked Jan 23 '12 18:01

JPhi1618


2 Answers

It looks like you are seeing the issue described on connect.microsoft.com.

There is a workaround suggested there, but it seems a bit nasty.

Other options that might help:

  1. Don't export std::string, instead use const char * in the DLL interface (see https://stackoverflow.com/a/5340065/12663)
  2. Ensure the _ITERATOR_DEBUG_LEVEL matches for all your projects
like image 76
danio Avatar answered Oct 30 '22 12:10

danio


Link to MS article that you presented says that some STL classes "...are already exported by the C Runtime DLL. Therefore, you cannot export them from your DLL. ". Including basic_string. And your link error says that basic_string symbol "...already defined in OtherClass.obj". Because linker sees two equal symbols in two different places.

like image 39
Vlad Novakovsky Avatar answered Oct 30 '22 13:10

Vlad Novakovsky