C++. Сборник рецептов - [15]

Шрифт
Интервал

Имеется два стандартных метода экспорта символов из Windows DLL.

• Использование атрибута >__declspec(dllexport) в заголовочных файлах DLL и сборка библиотеки импорта, предназначенной для применения при сборке кода, использующего эту DLL.

Атрибут >__dеclspec(dllexport) должен указываться в начале объявления экспортируемой функции или данных, вслед за какими-либо спецификаторами сборки, и сразу за ним должно следовать ключевое слово >class или >struct для экспортируемого класса. Это проиллюстрировано в примере 1.6. Заметьте, что >__declspec(dllexport) не является частью языка С++; это расширение языка, реализованное для большинства компиляторов для Windows.

• Создание файла .def, описывающего функции и данные, экспортируемые из динамической библиотеки.

Пример 1.6. Использование атрибута __declspec(dllexport)

>__declspec(dllexport) int m = 3; // Экспортируемое определение данных

>extern __declspec(dllexport) int n; // Экспортируемое объявление данных

>__declspec(dllexport) void f(); // Экспортируемое объявление функции class

>__declspec(dllexport) c { // Экспортируемое определение класса

> /* ... */

>};

Использование файла .def имеет несколько преимуществ — например, он может позволить осуществлять доступ к функциям в DLL по номеру, а не по имени, что сокращает размер DLL. Он также устраняет необходимость запутанных директив препроцессора, таких как показанные в примере 1.2 в заголовочном файле georgeringo.hpp. Однако он также имеет и несколько серьезных недостатков. Например, файл .def не может использоваться для экспорта классов. Более того, можно забыть обновить свой файл .def при добавлении, удалении или изменении функций в вашей DLL. Таким образом, я рекомендую вам всегда использовать >__declspec(dllexport). Чтобы изучить полный синтаксис файлов .def, а также научиться их использовать, обратитесь к документации по своему инструментарию.

Импорт символов из DLL

Как есть два способа экспорта символов из DLL, так есть и два способа импорта символов.

• В заголовочных файлах, включенных в исходный код, использующий DLL, используйте атрибут >__declspec(dllimport) и при сборке этого кода передайте библиотеку импорта компоновщику.

• При сборке кода, использующего DLL, укажите файл .def.

Как и в случае с экспортом символов, я рекомендую вместо файлов .def использовать в вашем исходном коде атрибут >__declspec(dllimport). Атрибут >__declspec(dllimport) используется точно так же, как и атрибут >__declspec(dllexport), обсуждавшийся ранее. Аналогично >__declspec(dllexport) атрибут >__declspec(dllimport) не является частью языка С++, а является расширением языка, реализованным для большинства компиляторов для Windows.

Если вы выбрали использование >__declspec(dllexport) и >__declspec(dllimport), вы должны убедиться, что при сборке DLL использовали >__declspec(dllexport), а при компиляции кода, использующего эту DLL, использовали >__declspec(dllimport). Одним из подходов является использование двух наборов заголовочных файлов: одного для сборки DLL, а другого для компиляции кода, использующего эту DLL. Однако это неудобно, так как сложно одновременно сопровождать две отдельные версии одних и тех же заголовочных файлов.

Вместо этого обычно используют определение макроса, который при сборке DLL расширяется как >__declspec(dllexport), а в противном случае — как >__declspec(dllimport). В примере 1.2 я использовал для этой цели макроопределение >GEORGERINGO_DECL. В Windows, если определен символ >GEORGERINGO_SOURCE, то >GEORGERINGO_DECL раскрывается как >__declspec(dllexport), а в противном случае — как >__declspec(dllimport). Описанный результат вы получите, определив >GEORGERINGO_SOURCE при сборке DLL libgeorgeringo.dll, но не определяя его при компиляции кода, использующего libgeorgeringo.dll.

Сборка DLL с помощью GCC

Порты GCC Cygwin и MinGW, обсуждавшиеся в рецепте 1.1, работают с DLL по-другому, нежели остальные инструменты для Windows. При сборке DLL с помощью GCC по умолчанию экспортируются все функции, классы и данные. Это поведение можно изменить, использовав опцию компоновщика --no-export-all-symbols, применив в исходных файлах атрибут >__declspec(dllexport) или используя файл .def. В каждом из этих трех случаев, если вы не используете опцию --export-all-symbols, чтобы заставить компоновщик экспортировать все символы, экспортируются только те функции, классы и данные, которые помечены атрибутом >__declspec(dllexport) или указаны в файле .def.

Таким образом, инструментарий GCC можно использовать для сборки DLL двумя способами: как обычный инструментарий Windows, экспортирующий символы явно с помощью >__declspec, или как инструментарий Unix, автоматически экспортирующий все символы[1]. В примере 1.2 и табл. 1.11 я использовал последний метод. Если вы выберете этот метод, вы должны в целях предосторожности использовать опцию --export-all-symbols — на тот случай, если у вас окажутся заголовки, содержащие >__declspec(dllexport).

GCC отличается от других инструментов для Windows и еще в одном: вместо того чтобы передавать компоновщику библиотеку импорта, связанную с DLL, вы можете передать саму DLL. Это обычно быстрее, чем использование библиотеки импорта. Однако это может привести к проблемам, так как в одной и той же системе может существовать несколько версий одной DLL, и вы должны быть уверены, что компоновщик выберет правильную версию. В табл. 1.11 при демонстрации того, как создавать библиотеки импорта с помощью GCC, я решил не использовать эту возможность.


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


MFC и OpenGL

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


Симуляция частичной специализации

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


Обработка событий в С++

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


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.