Как я могу использовать классы стандартной библиотеки (STL) в моем интерфейсе DLL или ABI? - PullRequest
60 голосов
/ 14 апреля 2011

Ранее было несколько вопросов об экспорте класса, содержащего классы stl, относительно визуального предупреждения студии C4251: Например, этот вопрос или этот вопрос. Я уже прочитал отличное объяснение на UnknownRoad.

Слепое отключение предупреждения кажется немного опасным, хотя это может быть вариантом. Оборачивать все эти классы std и экспортировать их тоже не вариант. В конце концов, она называется Standard Library Template ... То есть, каждый хочет предоставить интерфейс с этими стандартными классами.

Как я могу использовать stl-классы в моем dll-интерфейсе? Каковы общие практики?

1 Ответ

88 голосов
/ 14 апреля 2011

Запомните одну вещь, прежде чем читать дальше: Мой ответ исходит из точки зрения написания переносимого кода, который можно использовать в приложениях, составленных из модулей, скомпилированных в разных компиляторах. Это может включать разные версии или даже разные уровни исправлений одного и того же компилятора.

Как я могу использовать stl-классы в моем DLL-интерфейс?

Ответ: Вы часто не можете 1 .

Причина: STL - это библиотека кодов, а не двоичная библиотека, как DLL. У него нет ни одного ABI, который гарантированно будет одинаковым везде, где вы можете его использовать. Действительно, STL означает « Стандарт Библиотека шаблонов», но ключевое слово здесь, помимо Стандартного, - Шаблон .

Стандарт определяет методы и члены данных, которые должен предоставлять каждый класс STL, и определяет, что эти методы должны делать; но не более В частности, в стандарте не указано, как разработчики компиляторов должны реализовывать определенные стандартом функциональные возможности. Разработчики компиляторов могут предоставить реализацию класса STL, который добавляет функции-члены и переменные-члены , а не , перечисленные в Стандарте, при условии, что те члены, которые * определены в Стандарте, все еще там и делай то, что говорит Стандарт.

Может быть, пример в порядке. Класс basic_string определен в Стандарте как имеющий определенные функции-члены и переменные. Фактическое определение в Стандарте составляет почти 4 страницы, но вот его фрагмент:

namespace std {
  template<class charT, class traits = char_traits<charT>,
    class Allocator = allocator<charT> >
  class basic_string {
[snip]
  public:
    // 21.3.3 capacity:
    size_type size() const;
    size_type length() const;
    size_type max_size() const;
    void resize(size_type n, charT c);
    void resize(size_type n);
    size_type capacity() const;
    void reserve(size_type res_arg = 0);
    void clear();
    bool empty() const;
[snip]
};

Рассмотрим функции-члены size() и length(). В Стандарте нет ничего, что определяло бы переменные-члены для хранения этой информации. Действительно, переменные-члены не определены вообще, даже для хранения самой строки. Так как это реализовано?

Ответ - много разных способов. Некоторые компиляторы могут использовать переменную-член size_t для хранения размера и char* для хранения строки. Другой может использовать указатель на какое-то другое хранилище данных, которое хранит эти данные (это может иметь место в реализации с подсчетом ссылок). Фактически, разные версии или даже уровни исправлений одного и того же компилятора могут изменить эти детали реализации. Вы не можете положиться на них. Итак, реализация MSVC 10 может выглядеть так:

namespace std {
  template<class charT, class traits = char_traits<charT>,
    class Allocator = allocator<charT> >
  class basic_string {
[snip]
char* m_pTheString;
};

size_t basic_string::size() const { return strlen(m_pTheString;) }

... Тогда как MSVC 10 с пакетом обновления 1 может выглядеть следующим образом:

namespace std {
  template<class charT, class traits = char_traits<charT>,
    class Allocator = allocator<charT> >
  class basic_string {
[snip]
vector<char> m_TheString;
};

size_t basic_string::size() const { return m_TheString.size(); }

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

Теперь предположим, что вы используете MSVC10 для написания DLL, которая экспортирует этот класс:

class MyGizmo
{
public:
  std::string name_;
};

Что такое sizeof(MyGizmo)?

Предполагая мои предложенные реализации выше, под MSVC10 это будет sizeof(char*), но под SP1 это будет sizeof(vector<char>). Если вы напишите приложение в VC10 SP1, которое использует DLL, размер объекта будет выглядеть иначе, чем на самом деле. Двоичный интерфейс изменен.


Подробнее об этом см. Стандарты кодирования C ++ (Amazon ссылка ) № 63.


1 : " Вы часто не можете " На самом деле вы можете экспортировать компоненты стандартной библиотеки или любые другие компоненты библиотеки кода (например, Boost) с достаточной степенью надежности, когда вы иметь полный контроль над цепочками инструментов и библиотеками.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...