§ 8. Шаблоны и создание библиотек
01До этого в заданиях мы с вами писали программы, с которыми предполагается работать из командной строки путем запуска исполняемого файла через интерфейс операционной системы. Однако, с многими программами предполагается взаимодействовать путем динамического (реже статического) соединения их исполняемых файлов с исполняемыми файлами других программ. В таком случае программы называют библиотеками, поскольку в них отсутствует единая точка входа (функции main
), а взамодействие с их кодом происходит посредством вызова функций по имени.
Схема функционирования библиотек зависит от операционной системы. В Линуксе существует два типа библиотек:
- динамеческие — файлы с расширением
so
(англ. shared object) илиso.V.V.V
, гдеV
кодирует версию библиотеки, и - статические — файлы с расширением
a
(англ. archive).
Статические библиотеки представляют собой архив из объектных файлов, созданных компилятором для каждого файла с исходным кодом C++ (файлы с расширением cc
, C
, cpp
, cxx
и другие). Динамические библиотеки представляют собой исполняемый файл без функции main
и с некоторыми дополнительными полями, таких как версия библиотеки. Расширения файлов являются подсказкой для человека и компилятора, в общем случае можно использовать любые расширения, но тогда о типе файла придется сообщить компилятору посредством отдельного флага.
02Для линковки статической библиотеки достаточно указать полный путь к ней в командной строке линковщика: g++ file1.o file2.o other-files.a
. После этого линковщик будет использовать все объектные файлы из архива other-files.a
. Для создания самой статической библиотеки из предварительно скомпилированных файлов нужна команда ar
: ar -csr other-files.a file3.o file4.o
.
03Для линковки динамической библиотеки нужно указать ее имя с помощью флага -l
.
g++ file1.o file2.o -lother-files
Тогда линковщик будет искать в системных директориях файлы с названием libother-files.so
(файлы с версиями после расширения используются только после установки вашей программы в систему). Если файл найдет, то его содержимое будет использоваться для определения адресов функций, вызываемых по имени в объектных файлах file1.o
и file2.o
. Для того чтобы добавить директории в список для поиска библиотек, испольуется флаг -L
и переменная среды LD_LIBRARY_PATH
, которая должна содержать список директорий, разделенных двоеточием. После флагов -l
и -L
не рекомендуется использовать пробел для совместимости со старыми версиями компиляторов.
В Meson для создания статической или динамической библиотеки достаточно заменить executable
на static_library
или shared_library
соответственно и добавить необязательный параметр version
, остальные параметры этих функций такие же, как у executable
:
shared_library( 'other-files', sources: ['file3.cc', 'file4.cc'], version: meson.project_version() )
По соглашению имена файлов библиотек всегда начинаются с приставки lib
, поэтому в данном примере будет создана библиотека libother-files.so
, а также символьные ссылки на нее с указанием версии проекта.
Динамические библиотеки, от которых зависит программа, загружаются в память автоматически при запуске этой программы. Загрузка рекурсивна, так что, если библиотека зависит от других библиотек, то они тоже будут загружены автоматически. Загрузить различные версии одних и тех же библиотек одновременно в рамках одной программы невозможно, поскольку это бы привело к конфликту имен. Для обхода этого ограничения некоторые библиотеки добавляют к именам своих символов (функций и глобальных переменных) версию библиотеки. Так делает, например, библиотека libc
, которая является оберткой для системных вызовов Линукса. Версионирование символов библиотеки — довольно сложная операция, которой автору не приходилось заниматься, и она здесь не рассматривается.
Задания
- Напишите классы для геометрических фигур: окружность, прямоугольник. В каждом классе должны быть поля и методы для доступа к этим полям, которых достаточно для вычисления площади. Классы являются шаблонами с одним параметром — типом числа с плавающей точкой.
template <class T> class Circle; template <class T> class Rectangle;
В заголовочном файле должны быть объявлены только прототипы методов, реализации методов должны находиться вcc
-файле. Соберите из этих файлов динамическую библиотеку, в которой должны быть конкретизации шаблонов для чисел с плавающей точкой одинарной и двойной точности (float
иdouble
). - Подключите собранную библиотеку к программе и используйте классы для вывода площади произвольных фигур на консоль с одинарной и двойной точностью. Для подключения библиотеки к программе используйте параметр
link_with
:mylib = shared_library(...) executable('myapp', link_with: mylib, ...)
- Напишите функцию
my_count
, которая вычисляет количество элементов в контейнере, используя итераторы начала и конца (аналогstd::distance
). Эта функция должна работать с любым типом итераторов, даже если для них не определен оператор вычитания. Для итераторов с произвольным доступом должно использоваться простое вычитание, для остальных итераторов — перебор элементов. Для реализации используйтеstd::iterator_traits
иconstexpr if
(см. предыдущие слайды). - Напишите тест, которые проверит работу
my_count
для вектора и списка.