Экспортировать классы, содержащие СТД:: объекты (векторные, карты и т. д.) Из DLL



Я пытаюсь экспортировать классы из DLL, которые содержат такие объекты, как std::vectors и std::strings - весь класс объявляется как экспорт dll через:



    class DLL_EXPORT FontManager
{


проблема в том, что для членов сложных типов я получаю это предупреждение:




предупреждение ошибка c4251: 'FontManager::м__шрифтов' класс 'с std::map' должен иметь DLL-интерфейс, который будет использоваться клиентами класса 'FontManager'
с
[
_Kty=std:: string,
_Ty=tFontInfoRef
]




Я могу удалить некоторые предупреждения, поставив перед ними следующее объявление класса forward, даже если я не изменяю тип самих переменных-членов:



template class DLL_EXPORT std::allocator<tCharGlyphProviderRef>;
template class DLL_EXPORT std::vector<tCharGlyphProviderRef,std::allocator<tCharGlyphProviderRef> >;
std::vector<tCharGlyphProviderRef> m_glyphProviders;


похоже, что прямое объявление "вводит" DLL_EXPORT, когда член компилируется, но безопасно ли это? Действительно ли это что-то меняет, когда клиент компилирует этот заголовок и использует контейнер std на своей стороне? Будет ли это сделать все будущее использование такого контейнера DLL_EXPORT (и, возможно, не встроенного?)? И действительно ли это решает проблему, о которой предупреждает предупреждение?



является ли это предупреждение чем-то, о чем я должен беспокоиться, или было бы лучше отключить его в области этих конструкций? Клиенты и dll всегда будут построены с использованием одного и того же набора библиотек и компиляторов, и это только заголовочные классы...



Я использую Visual Studio 2003 со стандартным STD библиотека.



---- обновление ----



Я хотел бы нацелить вас больше, хотя, как я вижу, ответы являются общими, и здесь мы говорим о контейнерах и типах std (таких как std::string) - возможно, вопрос действительно таков:



можем ли мы отключить предупреждение для стандартных контейнеров и типов, доступных как для клиента, так и для dll через одни и те же заголовки библиотеки, и относиться к ним так же, как мы относимся к int или любому другому встроенному типу? (Это, кажется, работает правильно на мой сторона.) Если бы это были условия, при которых мы можем это сделать?



или, может быть, использование таких контейнеров должно быть запрещено или, по крайней мере, ультра заботиться о том, чтобы никакие операторы присваивания, конструкторы копирования и т. д. не были встроены в клиент dll?



В общем, я хотел бы знать, считаете ли вы, что проектирование интерфейса dll с такими объектами (и, например, использование их для возврата материала клиенту в качестве типов возвращаемых значений) является хорошей идеей или нет, и почему - я хотел бы имейте" высокоуровневый " интерфейс к этой функциональности... может быть, лучшим решением является то, что предложил Нил Баттерворт - создание статической библиотеки?

679   12  

12 ответов:

когда вы касаетесь члена в своем классе от клиента, нужно предоставить DLL-интерфейс. DLL-интерфейс означает, что компилятор создает функцию в самой DLL и делает ее импортируемой.

поскольку компилятор не знает, какие методы используются клиентами класса DLL_EXPORTED, он должен обеспечить, чтобы все методы экспортировались в dll. Он должен обеспечить, чтобы все члены, к которым могут получить доступ клиенты, также должны экспортировать свои функции в dll. Это происходит, когда компилятор предупреждает Вас о том, что методы не экспортируются, а компоновщик клиента отправляет ошибки.

Не каждый член должен быть отмечен с помощью dll-export, например, частные члены не могут быть затронуты клиентами. Здесь вы можете игнорировать/отключать предупреждения (остерегайтесь компилятора, генерируемого dtor/ctors).

в противном случае члены должны экспортировать свои методы. Прямое объявление их с помощью DLL_EXPORT не экспортирует методы этих классов. Вы должны отметить соответствующие классы в их компиляция-единица измерения как DLL_EXPORT.

к чему это сводится ... (для не экспортируемых членов dll)

  1. если у вас есть члены, которые не/не могут быть использованы клиентами, отключите предупреждение.

  2. если у вас есть члены, которые должны использоваться клиентами, создайте оболочку экспорта dll или создайте методы косвенного обращения.

  3. чтобы сократить количество внешних видимых элементов, используйте такие подходы, как идиому Pimpl.


template class DLL_EXPORT std::allocator<tCharGlyphProviderRef>;

это создает экземпляр специализации шаблона в текущей единице компиляции. Таким образом, это создает методы std::allocator в dll и экспортирует соответствующие методы. Это не работает для конкретных классов, это лишь конкретизация шаблонов классов.

Это предупреждение говорит вам, что пользователи вашей DLL не будут иметь доступа к вашим переменным-членам контейнера через границу DLL. Явный экспорт делает их доступными, но это хорошая идея?

В общем, я бы избегал экспорта контейнеров std из вашей DLL. Если вы можете абсолютно гарантировать, что ваша DLL будет использоваться с той же версией среды выполнения и компилятора, вы будете в безопасности. Вы должны убедиться, что память, выделенная в вашей DLL освобождается с помощью того же диспетчера памяти. К в противном случае будет, в лучшем случае, утверждать во время выполнения.

итак, не выставляйте контейнеры непосредственно через границы DLL. Если вам нужно предоставить элементы контейнера, сделайте это с помощью методов доступа. В случае, если вы предоставили, отделить интерфейс от реализации и разоблачить интерфейс на уровне файлов. Использование контейнеров std-это деталь реализации, к которой клиент вашей DLL не должен иметь доступа.

кроме того, сделайте то, что предлагает Нил, и создайте статический библиотека вместо библиотеки DLL. Вы теряете возможность загружать библиотеку во время выполнения, и потребители вашей библиотеки должны повторно связываться каждый раз, когда вы меняете свою библиотеку. Если это компромиссы, с которыми вы можете жить, статическая библиотека, по крайней мере, поможет вам преодолеть эту проблему. Я все равно буду утверждать, что вы раскрываете детали реализации излишне, но это может иметь смысл для вашей конкретной библиотеки.

есть и другие вопросы.

некоторые контейнеры STL "безопасны" для экспорта (например, vector), а некоторые нет (например, map).

карта, например, небезопасна, потому что она (в любом случае в дистрибутиве MS STL) содержит статический элемент с именем _Nil, значение которого сравнивается в итерации для проверки конца. Каждый модуль, скомпилированный с помощью STL, имеет другое значение для _Nil, и поэтому карта, созданная в одном модуле, не будет повторяться из другого модуля (он никогда не обнаруживает конец и взрывается).

Это будет применяться, даже если вы статически ссылаетесь на lib, так как вы никогда не можете гарантировать, что значение _Nil будет (оно неинициализировано).

Я считаю, что STLPort этого не делает.

лучший способ, который я нашел для обработки этого сценария:

создайте свою библиотеку, назвав ее компилятором и версиями stl, включенными в имя библиотеки, точно так же, как это делают библиотеки boost.

примеры:

- FontManager-msvc10-mt.dll для версии dll, специфичной для компилятора MSVC10, с stl по умолчанию.

- FontManager-msvc10_stlport-mt.dll для версии dll, специфичной для компилятора MSVC10, с stl порт.

- FontManager-msvc9-mt.dll для версии dll, специфичной для компилятора MSVC 2008, с STL по умолчанию

- libFontManager-msvc10-mt.Либ для статической версии lib, специфичной для компилятора MSVC10, с stl по умолчанию.

следуя этой схеме, вы избежите проблем, связанных с различными реализациями stl. помните, что реализация stl в vc2008 отличается от реализации stl в vc2010.

см. ваш пример с помощью boost:: config library:

#include <boost/config.hpp>

#ifdef BOOST_MSVC
#  pragma warning( push )
#  pragma warning( disable: 4251 )
#endif

class DLL_EXPORT FontManager
{
public:
   std::map<int, std::string> int2string_map;
}

#ifdef BOOST_MSVC
#  pragma warning( pop )
#endif

одна альтернатива, которую мало кто, похоже, рассматривает, - это вообще не использовать DLL, а статически связываться со статикой .Библиотека LIB. Если вы это сделаете, все проблемы экспорта/импорта исчезнут (хотя у вас все равно будут проблемы с искажением имен, если вы используете разные компиляторы). Вы, конечно, теряете функции архитектуры DLL, такие как загрузка функций во время выполнения, но во многих случаях это может быть небольшая цена.

нашел в этой статье. Короче говоря, у Аарона есть "реальный" ответ выше; не выставляйте стандартные контейнеры через границы библиотеки.

хотя этот поток довольно старый, я недавно нашел проблему, которая заставила меня снова подумать о наличии шаблонов в моих экспортированных классах:

Я написал класс, который имел закрытый член типа std:: map. Все работало довольно хорошо, пока он не был скомпилирован в режиме выпуска, даже при использовании в системе сборки, что гарантирует, что все параметры компилятора одинаковы для всех целей. Карта была полностью скрыта, и ничто не было открыто непосредственно клиентам.

Как a результат код просто сбой в режиме выпуска. Я догадываюсь, потому что разные двоичные экземпляры std::map были созданы для реализации и клиентского кода.

Я думаю, что стандарт C++ не говорит ничего о том, как это должно обрабатываться для экспортированных классов, поскольку это в значительной степени зависит от компилятора. Поэтому я думаю, что самое большое правило переносимости - это просто выставлять интерфейсы и использовать идиому PIMPL как можно больше.

Спасибо за любую просвещение

в таких случаях рассмотрим использование идиомы pimpl. Скрыть все сложные типы за одной пустотой*. Компилятор обычно не замечает, что ваши члены являются частными и все методы, включенные в DLL.

экспорт классов, содержащих std:: объекты (вектор, карта и т. д.) Из dll

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

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

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

и:

для экспорта класса, содержащего элемент данных, который является объектом STL

  1. как в DLL, так и в.exe-файл, ссылка на ту же версию DLL времени выполнения C. Либо связаться с библиотекой msvcrt.lib (release build) или свяжите оба с Msvcrtd.Либ (отладочное построение).
  2. в библиотеке DLL укажите _И _declspec спецификатор в объявлении инстанцирование шаблона для экспорта в STL при создании экземпляра класса с файл DLL.

    Примечание: Вы не можете пропустить Шаг 2. Вы должны экспортировать создание экземпляра класса STL, который используется для создания элемента данных.
  3. в библиотеке DLL укажите спецификатор _ _ declspec в объявлении класса для экспорта класса из библиотеки DLL.
  4. в рамках .exe-файл, укажите спецификатор _ _ declspec в объявлении класса импорт класса из DLL. Если класс, который вы экспортируете, имеет один или несколько базовых классов, тогда вы должны экспортируйте также базовые классы.

    если класс, который вы экспортируете содержит элементы данных, которые имеют тип класса, затем необходимо экспортировать классы членов данных, а также.

Если вы используете DLL, сделайте инициализацию всех объектов в событии "DLL PROCESS ATTACH" и экспортируйте указатель на его классы/объекты.
Вы можете предоставить определенные функции для создания и уничтожения объектов, а также функции для получения указателя на созданные объекты, чтобы вы могли инкапсулировать эти вызовы в класс-оболочку доступа в include file.

ни один из описанных выше обходных путей не приемлем для MSVC из-за статических элементов данных внутри классов шаблонов, таких как контейнеры stl

каждый модуль (dll / exe) получает свою собственную копию каждого статического определения...Ух ты! это приведет к ужасным вещам, если вы каким-то образом "экспортируете" такие данные (как "указано" выше)...поэтому не пытайтесь сделать это дома

см.http://support.microsoft.com/kb/172396/en-us

лучший подход для использования в таких сценариях-использовать шаблон дизайна PIMPL.

Comments

    Ничего не найдено.