Подтвердить что ты не робот

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

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

    class DLL_EXPORT FontManager
{

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

warning C4251: 'FontManager:: m__fonts': class 'std:: map < _Kty, _Ty > ' должен иметь dll-интерфейс, который будет использоваться клиентами класса 'FontManager'         с         [             _Kty = std::string,             _Ty = tFontInfoRef         ]

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

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-интерфейса, в котором такие объекты (и, например, использование их для возврата материала клиенту в качестве типов возвращаемых значений), является хорошей идеей или нет, и почему-бы я например, иметь интерфейс "высокого уровня" для этой функциональности... возможно, лучшим решением является то, что предложил Нил Баттерворт - создание статической библиотеки?

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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


template class DLL_EXPORT std::allocator<tCharGlyphProviderRef>;

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

Ответ 2

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

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

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

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

Ответ 3

Существуют и другие проблемы.

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

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

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

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

Ответ 4

Лучший способ найти этот сценарий:

создайте свою библиотеку, назовите ее с помощью компилятора и 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 для статической версии 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

Ответ 5

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

Ответ 6

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

Ответ 7

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

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

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

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

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

Ответ 8

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

Ответ 9

Экспорт классов, содержащих std:: objects (вектор, карта и т.д.) из dll

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

Экспортировать класс STL

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

и

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

  • Как в DLL, так и в файле .exe, ссылка на ту же версию DLL версии C. Либо свяжите оба с Msvcrt.lib(выпускная сборка) или свяжите оба с Msvcrtd.lib(сборка отладки).
  • В DLL укажите спецификатор __declspec в объявлении создания шаблона, чтобы экспортировать экземпляр класса STL из DLL.

    ПРИМЕЧАНИЕ. Вы не можете пропустить шаг 2. Вы должны экспортировать создание экземпляра класса STL, который используется для создания элемента данных.
  • В DLL укажите спецификатор __declspec в объявлении класса для экспорта класса из DLL.
  • В .exe файле укажите спецификатор __declspec в объявлении класса для импорта класса из DLL. Если класс, который вы экспортируете, имеет один или несколько базовых классов, тогда вы должны экспортировать базовые классы.

    Если класс, который вы экспортируете содержит элементы данных, относящиеся к типу класса, тогда вы должны экспортировать классы элементов данных также.

Ответ 10

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

Ответ 11

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

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

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

Ответ 12

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