При экспорте шаблона STL std :: basic_string из DLL я получаю ошибку LNK2005 - PullRequest
10 голосов
/ 23 января 2012

ОК, поэтому я прочитал несколько вопросов и статей на эту тему, и я чувствую, что понимаю основы, но у меня все еще проблемы.

У меня есть DLL, которая экспортирует класс, в качестве члена которого используется std :: string. Моя основная программа содержит классы, которые также имеют строки, и использует DLL.

Если я скомпилирую DLL в VS2010, я получу следующие предупреждения:

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

Когда я компилирую EXE, я получаю те же предупреждения, но ошибок нет, и программа компилируется и запускается. На самом деле, это большой проект, поэтому я получаю около 40 предупреждений, и я не слишком заинтересован в этом. (В качестве дополнительного наблюдения эти предупреждения отсутствуют при компиляции с VS2008)

Итак, я прочитал об этом предупреждении, и оно привело меня к этой статье MS: http://support.microsoft.com/default.aspx?scid=KB;EN-US;168958 в котором рассказывается, как экспортировать шаблон STL из DLL, чтобы получить предупреждения, которые я получал.

Проблема в том, что, когда я добавляю следующие строки для удаления предупреждений:

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

DLL компилируется без предупреждений, но когда я компилирую свой EXE, компоновщик подбрасывает:

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

И DLL, и EXE компилируются с одинаковыми параметрами генерации кода. Я могу использовать MT на обоих или MD, и результаты одинаковы.

Я включаю код из свернутой программы-примера на случай, если что-то упущу выше.

Мой главный вопрос: можно ли исправить ошибки LNK2005 или можно просто игнорировать предупреждения C4251?

Редактировать: Так что я прочитал немного больше, и похоже, что если std :: string, который использует класс DLL, является закрытой переменной, доступ к которой имеют только функции-члены, это может быть безопасно игнорировать предупреждение ... Есть комментарии? Это шаг в правильном направлении?

код DLL:

#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-код:

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

using namespace std;

void main()
{
    MyClass class1;

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

}

Ответы [ 5 ]

3 голосов
/ 10 ноября 2013

Ссылка на статью MS, которую вы представили, говорит, что некоторые классы STL "... уже экспортированы DLL времени выполнения C. Следовательно, вы не можете экспортировать их из вашей DLL.". Включая basic_string. И ваша ошибка ссылки говорит, что символ basic_string "... уже определен в OtherClass.obj". Поскольку компоновщик видит два одинаковых символа в двух разных местах.

3 голосов
/ 11 сентября 2012

Похоже, вы видите проблему, описанную на connect.microsoft.com .

Там предлагается обходной путь, но он кажется немного неприятным.

Другие варианты, которые могут помочь:

  1. Не экспортируйте std :: string, вместо этого используйте const char * в интерфейсе DLL (см. https://stackoverflow.com/a/5340065/12663)
  2. Убедитесь, что _ITERATOR_DEBUG_LEVEL соответствует всем вашим проектам
0 голосов
/ 25 сентября 2016

Для меня вся тема сводилась к

  • Не экспортируйте STL.Не обращайте внимания на предупреждение.(По крайней мере, до MSVC2013.)
  • Конечно, убедитесь, что каждая сторона ссылается на среду выполнения C одинаково в отношении отладки / выпуска, статической / динамической.

ВсегдаУстранена проблема для меня.

К сожалению, это не ответ, если у вас нет контроля над исходным кодом, на который вы хотите сослаться.

0 голосов
/ 31 марта 2016

У меня есть хак, который может исправить это при температуре

Откройте «Параметры проекта», нажмите «Линкер» -> «Командная строка». В поле «Дополнительные параметры» введите

 /FORCE:MULTIPLE
0 голосов
/ 08 апреля 2015

При экспорте шаблона STL std :: basic_string из DLL я получаю ошибку LNK2005

См. Также статью Microsoft KB 168958 Как экспортировать создание экземпляра класса Standard Template Library (STL) и класса, который содержит член данных, являющийся объектом STL . Из статьи:

Для экспорта класса STL

  1. И в DLL-файле, и в EXE-файле свяжите с одной и той же DLL-версией среды выполнения C. Либо свяжите оба с Msvcrt.lib (сборка релиза), либо связать оба с Msvcrtd.lib (отладочная сборка).
  2. В DLL укажите спецификатор __declspec в объявлении экземпляра шаблона, чтобы экспортировать экземпляр класса STL из DLL.
  3. В файле .exe укажите спецификаторы extern и __declspec в объявлении создания экземпляра шаблона, чтобы импортировать класс из DLL. Это приводит к предупреждению C4231 "нестандартное расширение используется: 'extern' перед явным созданием шаблона. "Вы можете игнорировать это предупреждение.
...