Создание статической библиотеки (VS)

В этой инструкции описан процесс создания и подключения собственных библиотек в Visual Studio 2019/2017.
Сборка библиотеки
- Создайте проект Visual Studio с типом Статическая библиотека (находится в разделе Visual C++ — Классические приложения).
- Добавьте в ваш проект заголовочные файлы и файлы с реализацией классов и функций, которые нужно собрать в библиотеку. Например, создадим файлы TestClass.h и TestClass.cpp:
#ifndef _TESTCLASS_H_ #define _TESTCLASS_H_ #include class TestClass < public: int echo(int); void helloWorld(); >; #endif // !_TESTCLASS_H_
#include "TestClass.h" int TestClass::echo(int number) < return number; >void TestClass::helloWorld()
- [по умолчанию]/Debug , если вы выбрали платформу сборки x86 (Win32) и конфигурацию Debug
- /Release , если вы выбрали платформу сборки x86 (Win32) и конфигурацию Release
- /x64/Debug , если вы выбрали платформу сборки x64 и конфигурацию Debug
- /x64/Release , если вы выбрали платформу сборки x64 и конфигурацию Release
- // , если у вас настроена нестандартная конфигурация сборки
Подключение библиотеки в проект
- Добавьте в файлы проекта собранную библиотеку lib.
- Подключите заголовочные файлы библиотеки в коде вашего проекта (или скопируйте их и добавьте в файлы проекта). Если проект с библиотекой и проект с использующим ее приложением находятся в одном решении, то заголовки библиотеки находятся в каталоге уровнем выше: ../ . Например:
#include #include "../MyLib/TestClass.h" int main()
Другой вариант: вы можете перейти в настройки проекта, раздел Каталоги VC++, и добавить в дополнительные каталоги подключаемых файлов в начало путь к папке с проектом библиотеки (не забудьте поставить точку с запятой в конце), например: C:/Projects/MySolution/MyLib; (если проект с библиотекой и проект с использующим ее приложением находятся в одном решении, то заголовки библиотеки находятся в каталоге уровнем выше: ../MyLib; ). После этого вы сможете подключать заголовочные файлы без указания пути к каталогам:
#include #include "TestClass.h" int main()
Не забудьте, что после внесения изменений в код файлов библиотеки ее необходимо собирать заново, чтобы она исправно функционировала в использующих ее приложениях. Для этого в обозревателе решений щелкните правой кнопкой мыши по названию проекта с библиотекой и выберите Пересобрать.
Создание статической библиотеки
Библиотека – это пакет кода, который предназначен для повторного использования многими программами. Обычно библиотека C++ состоит из двух частей:
- заголовочный файл, который определяет функциональность, которую библиотека предоставляет (предлагает) программам, использующим ее;
- предварительно скомпилированный двоичный файл, который содержит реализацию этой функциональности, предварительно скомпилированную в машинный код.
Некоторые библиотеки могут быть разделены на несколько файлов и/или иметь несколько файлов заголовков.
Библиотеки предварительно скомпилированы по нескольким причинам:
- Во-первых, поскольку библиотеки меняются редко, их не нужно часто перекомпилировать. Было бы пустой тратой времени перекомпилировать библиотеку каждый раз, когда вы пишете программу, которая ее использует.
- Во-вторых, поскольку предварительно скомпилированные объекты представлены машинным кодом, люди не могут получить доступ к исходному коду или изменить его, что важно для предприятий или людей, которые не хотят делать свой исходный код доступным из соображений интеллектуальной собственности.
Существует два типа библиотек: статические библиотеки и динамические библиотеки.
Статическая библиотека
Статическая библиотека (иногда называемая archive, «архив») состоит из подпрограмм, которые скомпилированы и линкуются непосредственно с вашей программой. Когда вы компилируете программу, использующую статическую библиотеку, все функции статической библиотеки, которые использует ваша программа, становятся частью вашего исполняемого файла. В Windows статические библиотеки обычно имеют расширение .lib , а в Linux – расширение .a (archive, архив).
Одним из преимуществ статических библиотек является то, что вам нужно распространять только исполняемый файл, чтобы пользователи могли запускать вашу программу. Поскольку библиотека становится частью вашей программы, это гарантирует, что с вашей программой всегда будет использоваться правильная версия библиотеки. Кроме того, поскольку статические библиотеки становятся частью вашей программы, вы можете использовать их так же, как функции, которые вы написали для своей программы. С другой стороны, поскольку копия библиотеки становится частью каждого исполняемого файла, который ее использует, это может привести к потере большого количества места. Статические библиотеки также не могут быть легко обновлены – для обновления библиотеки необходимо заменить весь исполняемый файл.
Динамическая библиотека
Динамическая библиотека (также называемая shared library, «общая библиотека») состоит из подпрограмм, которые загружаются в ваше приложение во время выполнения. Когда вы компилируете программу, использующую динамическую библиотеку, библиотека не становится частью вашего исполняемого файла – она остается отдельной единицей. В Windows динамические библиотеки обычно имеют расширение .dll (dynamic link library, библиотека динамической компоновки), а в Linux – расширение .so (shared object, общий объект).
Одним из преимуществ динамических библиотек является то, что многие программы могут совместно использовать одну копию библиотеки, что экономит место. Возможно, большим преимуществом является то, что динамическую библиотеку можно обновить до более новой версии без замены всех исполняемых файлов, которые ее используют.
Поскольку динамические библиотеки не связаны с вашей программой, программы, использующие динамические библиотеки, должны явно загружать и взаимодействовать с динамической библиотекой. Этот механизм может сбивать с толку и затруднять взаимодействие с динамической библиотекой. Чтобы упростить использование динамических библиотек, можно использовать библиотеку импорта.
Библиотека импорта
Библиотека импорта – это библиотека, которая автоматизирует процесс загрузки и использования динамической библиотеки. В Windows это обычно делается с помощью небольшой статической библиотеки ( .lib ) с тем же именем, что и динамическая библиотека ( .dll ). Статическая библиотека подключается к программе во время компиляции, и затем функциональные возможности динамической библиотеки можно эффективно использовать, как если бы это была статическая библиотека. В Linux файл общих объектов ( .so ) выполняет функции динамической библиотеки и библиотеки импорта. Большинство компоновщиков при создании динамической библиотеки могут создать библиотеку импорта для этой динамической библиотеки.
И как это применить к нашему проекту?
При выполнении лабораторных работ от вас будут требовать декомпозировать ваш код на связанные логические модули и представлять их виде библиотек. Эти библиотеки необходимо будет располагать в директории лабораторной работы, задание которой вы выполняете. Поэтому для наших целей будет подходить создание статической библиотеки.
Давайте на примере обычной библиотеки, умеющей только складывать несколько чисел, рассмотрим как делать статические библиотеки для выполнения лабораторных работ.
Создание проекта статической библиотеки
В директории создадим поддиректорию mymath .
В ней создадим файлы CMakeLists.txt , mymath.cpp и mymath.h . В итоге наша директория с лабораторной работой будет выглядеть следующим образом:
lab1 ├── mymath │ ├── CMakeLists.txt │ ├── mymath.cpp │ └── mymath.h ├── tests ├── CMakeList.txt ├── README.md └── main.cpp
Содержимое mymath.cpp
Оставляем код без комментариев:
#include "mymath.h" namespace mymath int sum(int a, int b) return a + b; > >
Содержимое mymath.h
Про заголовочные файлы вроде из предыдущей дисциплины вам должно быть известно?). Поэтому код тоже без комменатриев:
#ifndef MYMATH_H #define MYMATH_H namespace mymath int sum(int a, int b); > #endif //MYMATH_H
На всякий случай #ifndef — это про include guard:
Как только заголовок включен, он проверяет, определено ли уникальное значение. Затем, если он не определен, он определяет его и переходит к остальной части кода.
Когда код включается снова, первый #ifndef завершается ошибкой, в результате чего получается пустой файл.
Это предотвращает двойное объявление любых идентификаторов, таких как типы, перечисления и статические переменные.
Благодарим за такую познавательную информацию автора ответа на Stack Overflow.
И самое интересное, CMakeLists.txt
Вот он, виновник торжества:
cmake_minimum_required(VERSION 3.23) set(project "mymath") project($) set(CMAKE_CXX_STANDARD 17) set($_SOURCES mymath.cpp) set($_HEADERS mymath.h) set($_SOURCE_LIST $<$_SOURCES> $<$_HEADERS>) add_library($ STATIC $<$_SOURCE_LIST>)
Тут явно решили выделить название проекта в отдельную переменную окружения CMake с помощью вызова функции set(project «mymath») .
Переменные можно определить путём вызова команды set, а удалить вызовом unset. Получить значение переменной можно по конструкции $ . Если переменная ещё не определена и где-то потребовалось получить её значение, то данная переменная обратится в пустую строку.
Мы все еще не упоминали, что CMake имеет относительно простой интерпретируемый императивный язык сценариев, поддерживающий переменные, методы обработки строк, массивы, объявления функций и макросов? Нет?! Ну вот теперь живите с этой информацией.
Далее мы устанавливаем переменные окружения $_HEADERS , $_SOURCES и $_SOURCE_LIST . Тут явное перечисление то, какие исходные файлы и заголовочные файлы мы подключаем. Можно поиск этих файлов черех GLOB , но это считается антипаттерном. В этом выступлении объясняется почему. Вкратце: если мы добавляем новый файл, то нам явно нужно переписать CMakeLists.txt , а он в свою очередь начнет перегенировать свой проект.
Команда add_library компилирует библиотеку с указанным видом и именем из исходников. Важно отметить, что окончательное имя библиотеки зависит от целевой платформы (например, lib.a или .lib ).
О том, зачем указывать в add_library и add_executable в заголовочных файлах в CMake-файлах, пояснили тут.
Ну вот и все! Остается эту библиотеку подключить к нашему консольному приложению.
3.2 Создание статической библиотеки
Для создания статических библиотек существует специальная простая программа называемая ar (сокр. от archiver — архиватор). Она используется для создания, модификации и просмотра объектных файлов в статических библиотеках, которые в действительности представляют из себя простые архивы.
Создадим из файлов f1.c и f2.c отдельную библиотеку.
Для начала компилируем эти файлы:
olya:~# gcc -c f1.c f2.c
В результате получим, как обычно, два файла — f1.o и f2.o. Для того, чтобы создать библиотеку из объектых файлов надо вызвать программу ar со следующими параметрами:
ar rc libимя_библиотеки.a [список_*.o_файлов]
Допустим наша библиотека будет называться fs, тогда команда запишется в виде:
olya:~# ar rc libfs.a f1.o f2.o
В результате получим файл libfs.a, в котором будут лежать копии объектых файлов f1.o и f2.o. Если файл библиотеки уже существует, то архиватор будет анализировать содержимое архива, он добавит новые объектные файлы и заменит старые обновленными версиями. Опция c заставляет создавать (от create) библиотеку, если ее нет, а опция r (от replace) заменяет старые объектные файлы новыми версиями.
Пока у нас есть лишь архивный файл libfs.a. Чтобы из него сделать полноценную библиотеку объектных файлов надо добавить к этому архиву индекс символов, т.е. список вложенных в библиотеку функций и переменных, чтобы линковка происходила быстрее. Далается это командой:
Программа ranlib добавит индекс к архиву и получится полноценная статическая библиотека объектных файлов. Стоит отметить, что на некоторых системах программа ar автоматически создает индекс, и использование ranlib не имеет никакого эффекта. Но тут надо быть осторожным при атоматической компиляции библиотеки с помощью файлов makefile, если не использовать утилиту ranlib, то возможно на каких-то системах библиотеки будут создаваться не верно и потеряется независимость от платформы. Так что возьмем за правило тот факт, что утилиту ranlib надо запускать в любом случае, даже если он нее нет никакого эффекта.
Для компиляции нашего основного файла main.c надо сообщить компилятору, что надо использовать библиотеки. Чтобы компилятор знал где искать библиотеки ему надо сообщить каталог, в котором они содержатся и список этих билиотек. Каталог с библиотеками указывается ключом -L, в нашем случае библиотека находится в текущем каталоге, значит путь до нее будет в виде точки (-L.). Используемые библиотеки перечисляются через ключ -l, после которого указывается название библиотеки без префикса lib и окончания .a. В нашем случае этот ключ будет выглядеть, как -lfs. Теперь все одной командой:
olya:~# gcc -c main.c
olya:~# gcc main.o -L. -lfs -o rezult
Или можно чуть короче:
olya:~# gcc main.c -L. -lfs -o rezult
Заметьте, что компилятору нужны библиотеки на этапе создания конечного файла, т.е. линковки. В первом случае процесс компиляции совершается первой командой, а сборка файла второй командой. Если же мы попытаемся подсунуть библиотеку на этапе компиляции, то получим вежливый ответ:
olya:~# gcc -c main.c -L. -lfs
gcc: -lfs: linker input file unused since linking not done
Что означает, что файлы библиотек не нужны, до процесса линковки. Данная команда создаст лишь файл main.o, который в итоге потом придется собирать отдельно.
Статическая линковка пользовательских библиотек и библиотек Visual Studio
Сложно задать вопрос, однако, начну. Задача: нужно написать приложение (любое на Visual c++, но НЕ С++/CLI) с подключением lib файла, а на выходе получить один лишь exe, который бы запускался на любом компьютере с ОС Windows, имел в себе Lib файл (написанный пользователем, например), и не требовал бы наличия библиотек Visual Studio. Для нашего конечного проекта, к которому мы подключили lib-файлы, нужна статическая линковка. Конкретнее, в настройках проекта нужно выставить: C/C++ -> Создание кода -> Библиотека времени выполнения для отладочной версии /MTd, для релизной — /MT. Хорошо, теперь рассмотрю 2 случая, из которых вытекают мои вопросы. Случай 1: Мы пишем свой Lib файл Тогда как компилировать свой Lib файл? Нужно также скомпилировать его с /MTd или /MT или можно как-то иначе? Случай 2: Мы работаем со сторонней библиотекой, которую нашли в интернете и подключили Если lib файл, написанный нами мы можем как-то пересобрать, то что делать в этой ситуации? Еще вопрос: А если lib-файл вообще скомпилирован на другом компиляторе, например, MinGW, то есть ли вообще возможность подключить библиотеку к нашему проекту? Надеюсь, я смог выразиться понятно. Если кто-то сталкивался с подобным, опишите подробно процесс линковки. Варианты в стиле: «зачем lib? Сделай dll», отпадают априори!
Отслеживать
23.8k 3 3 золотых знака 47 47 серебряных знаков 61 61 бронзовый знак
задан 26 окт 2016 в 10:45
63 1 1 серебряный знак 6 6 бронзовых знаков
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
- По идее, компилируйте lib-файл с /MT или /MTd, в зависимости от платформы. В любом случае на этапе генерации lib-файла линковка не произойдёт.
- Если у вас сторонняя библиотека, которая идёт в виде исходников, то соберите её в lib, да.
- Lib, собранный другим компилятором, скорее всего не подойдёт, т. к. на lib (в отличие от DLL) нету бинарного стандарта. Если есть исходники, перекомпилируйтесь под Visual Studio.
Отслеживать
ответ дан 26 окт 2016 в 11:00
206k 28 28 золотых знаков 293 293 серебряных знака 526 526 бронзовых знаков
Какой-то у Вас неуверенный ответ 🙂 В первом никаких идей: для MT и MD используются разные макросы, и студия помещает в объектник упоминание библиотеки, с которой ему надо линковаться, поэтому, хотя линковки и не происходит, эти флаги должны соответствовать обязательно. По 3-му: совершенно точно не подойдут, т.к. объектные файлы даже разных студий могут не работать друг с другом, а тут MinGW
26 окт 2016 в 11:17
@ixSci: Я уже давно не работал с нативным кодом, так что позабыл тамошние проблемы 🙂 MinGW могла бы генерировать совместимые имена хотя бы для extern «C» -кода, но вроде бы она даже этого не делает. (Но не поручусь.)
26 окт 2016 в 11:20
Если не ошибаюсь, то даже extern «C» не поможет: к примеру, пусть у нас будет одна функция, в которой есть wchar_t и мы приплыли: в студии он 2 байта, а в gcc — 4
