Как выставить список STL за пределы библиотеки DLL?
Вопрос
У меня есть библиотека DLL, которой необходимо получить доступ к данным, хранящимся в контейнерах STL в главном приложении.Поскольку C ++ не имеет стандартного ABI, и я хочу поддерживать разные компиляторы, интерфейс между приложением и библиотекой DLL в основном должен оставаться обычным для старых данных.
Для векторов это относительно просто.Вы можете просто вернуть блок памяти вектора, потому что он гарантированно будет непрерывным:
// To return vector<int> data
virtual void GetVectorData(const int*& ptr, size_t& count) const
{
if (!vec.empty())
ptr = &(vec.front());
count = vec.size();
}
Теперь библиотека DLL может иметь безопасный доступ только для чтения к данным вектора через этот интерфейс.Библиотека DLL также может обернуть это, чтобы скопировать содержимое в вектор и для себя.
Однако как насчет списков STL (и deques)?Есть ли другой простой способ разрешить доступ через границу DLL?Или мне придется прибегнуть к какому-то интерфейсу getFirst() / GetNext()?Возможно, мне придется сделать это для множества списков, поэтому было бы неплохо иметь такое же простое решение, как у vector.
Решение
Возможно, вы можете передать что-то вроде «дескрипторов» итераторам списка/дека?Эти типы дескрипторов будут непрозрачными и будут объявлены в заголовочном файле, который вы отправите пользователям.Внутри вам нужно будет сопоставить значения дескриптора с итераторами списка/отключения.По сути, пользователь должен написать такой код:
ListHandle lhi = GetListDataBegin();
const ListHandle lhe = GetListDataEnd();
while (lhi != lhe)
{
int value = GetListItem(lhi);
...
lhi = GetNextListItem(lhi);
}
Другие советы
Вы можете передавать объекты stl между DLL и поддерживать разные компиляторы, если будете осторожны при создании экземпляра каждого типа stl.Вам нужны интеллектуальные макросы «DLLEXPORT». Я использую следующий набор для успешной поддержки VC и gcc.
#ifdef WIN32
#ifdef MYDLLLIB_EXPORTS // DLL export macros
#define MYDLLLIB_API __declspec(dllexport)
#define MYDLLLIB_TEMPLATE
#else
#define MYDLLLIB_API __declspec(dllimport)
#define MYDLLLIB_TEMPLATE extern
#endif
#else // Not windows --- probably *nix/bsd
#define MYDLLLIB_API
#ifdef MYDLLLIB_EXPORTS
#define MYDLLLIB_TEMPLATE
#else
#define MYDLLLIB_TEMPLATE extern
#endif
#endif // WIN32
При компиляции DLL определите MYDLLLIB_EXPORTS.Затем в DLL вы можете создать экземпляр каждого типа stl, который вы хотите использовать, например, списки или векторы строк.
MYDLLLIB_TEMPLATE template class MYDLLLIB_API std::vector<std::string>;
MYDLLLIB_TEMPLATE template class MYDLLLIB_API std::list<std::string>;
Потребители вашей DLL (у которых не определен MYDLLLIB_EXPORTS) увидят
extern template class __declspec(dllimport) std::vector<std::string>;
и использовать двоичный код, экспортированный из вашей DLL, вместо создания собственного экземпляра.
интерфейс между приложением и библиотекой DLL в основном должен оставаться обычными старыми данными.
Не обязательно.Вы должны быть уверены, что используется одна и та же версия компилятора.Кроме того, параметры сборки, влияющие на расположение объектов STL, точно одинаковы для библиотеки dll и приложения.
Если бы вы собирались выпустить dll на волю, вы были бы правы, если бы беспокоились о том, чтобы раскрыть STL за пределами dll-границ.Однако, если все находится под вашим контролем и чисто внутренним (или если вы можете жестко применять настройки сторонней сборки / компилятора), все должно быть в порядке.