Последствия использования std::vector в экспортированной dll-функции

У меня есть два экспортированных dll класса A и B. Объявление содержит функцию, которая использует std::vector в своей сигнатуре, например:

class EXPORT A{
 // ...
 std::vector<B> myFunction(std::vector<B> const &input);
};

(EXPORT - это обычный макрос, который должен быть установлен на _declspec (dllexport)/_ declspec (dllimport) соответственно.)

Чтение проблем, связанных с использованием классов STL в интерфейсе DLL, я собираю в резюме:

  • Использование std::vector в интерфейсе DLL потребует, чтобы все клиенты этой DLL были скомпилированы с той же версией одного и того же компилятора, потому что контейнеры STL не совместимы с бинарными. Хуже того, в зависимости от использования этой DLL-клиентами совместно с другими DLL-интерфейсом, "нестабильный" DLL-API может разорвать эти клиентские приложения при установке обновлений системы (например, пакетов Microsoft KB) (действительно?).

  • Несмотря на вышесказанное, если требуется, std::vector можно использовать в DLL API, экспортируя std::vector<B> как:

    template class EXPORT std::allocator<B>;
    template class EXPORT std::vector<B>;
    

    хотя это обычно упоминается в контексте, когда вы хотите использовать std::vector в качестве члена A (http://support.microsoft.com/kb/168958).

  • В следующей статье технической поддержки Microsoft обсуждается, как получить доступ к std::vector объектам, созданным в DLL, с помощью указателя или ссылки из исполняемого файла (http://support.microsoft.com/default.aspx?scid=kb; EN-US; Q172396). Вышеупомянутое решение использовать template class EXPORT ... также применимо. Однако недостаток, суммированный под первой точкой, кажется, остается.

  • Чтобы полностью избавиться от проблемы, нужно было бы обернуть std::vector и изменить подпись myFunction, PIMPL и т.д.

Мои вопросы:

  • Является ли приведенное выше резюме правильным, или я пропустил здесь что-то существенное?

  • Почему компиляция моего класса 'A' не генерирует предупреждение C4251 (класс 'std::vector < _Ty > ' должен иметь dll-интерфейс, который будет использоваться клиентами...)? У меня нет предупреждений о компиляторе, и я не получаю предупреждения об использовании std::vector в myFunction в экспортированном классе A (с VS2005).

  • Что нужно сделать, чтобы правильно экспортировать myFunction в A? Можно ли просто экспортировать std::vector<B> и B распределитель?

  • Каковы последствия возврата std::vector по значению? Предполагая исполняемый файл клиента, который был скомпилирован с другим компилятором (-версия). Сохраняется ли проблема при возврате значения, когда вектор скопирован? Я думаю да. Аналогично, для передачи std::vector в качестве постоянной ссылки: мог ли доступ к std::vector<B> (который мог быть создан исполняемым файлом, скомпилированным с другим компилятором (-версия)), приводит к проблемам в myFunction? Я думаю, да еще раз.

  • Является ли последняя точка маркера, указанная выше, действительно единственным чистым решением?

Большое спасибо за ваши отзывы.

Ответ 1

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

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

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

Ответ 2

У меня была такая же проблема и я нашел для нее аккуратное решение.
Вместо передачи std:vector вы можете передать QVector из библиотеки Qt.
Проблемы, которые вы цитируете, затем обрабатываются внутри библиотеки Qt, и вам не нужно вообще разбираться с ней.
Конечно, стоит использовать библиотеку и принять ее немного худшую производительность.
Что касается количества времени кодирования и отладки, которое это экономит, это решение стоит того.