Каковы преимущества библиотеки только для заголовков и почему вы пишете ее таким образом, чтобы не помещать реализацию в отдельный файл?
Существуют ситуации, когда библиотека только для заголовка является единственным вариантом, например, при работе с шаблонами.
Наличие библиотеки только для заголовков также означает, что вам не нужно беспокоиться о различных платформах, где может использоваться библиотека. Когда вы разделяете реализацию, вы обычно делаете это, чтобы скрыть детали реализации и распространять библиотеку как комбинацию заголовков и библиотек (lib
, dll
или .so
файлы). Они, конечно, должны быть скомпилированы для всех различных операционных систем / версий, которые вы предлагаете поддержку.
Вы также можете распространять файлы реализации, но это будет означать дополнительный шаг для пользователя — компиляция вашей библиотеки перед ее использованием.
Конечно, это относится к От случая к случаю основа. Например, библиотеки только для заголовков иногда увеличиваются размер кода & время компиляции.
Преимущества библиотеки только для заголовков:
Недостатки библиотеки только для заголовков:
Большие объектные файлы. Каждый встроенный метод из библиотеки, который используется в некотором исходном файле, также получит слабый символ, вне определения строки в скомпилированном объектном файле для этого исходного файла. Это замедляет компилятор, а также замедляет компоновщик. Компилятор должен сгенерировать все это раздувание, а затем компоновщик должен отфильтровать его.
Более длинная компиляция. В дополнение к упомянутой выше проблеме раздувания, компиляция займет больше времени, потому что заголовки по своей природе больше с библиотекой только для заголовков, чем скомпилированной библиотекой. Эти большие заголовки нужно будет проанализировать для каждого исходного файла, который использует библиотеку. Другим фактором является то, что эти заголовочные файлы в библиотеке только заголовков должны #include
заголовки, необходимые для встроенных определений, а также заголовки, которые были бы необходимы, если бы библиотека была скомпилирована.
Более запутанная компиляция. Вы получаете гораздо больше зависимостей с библиотекой только для заголовка из-за этих дополнительных #include
Требуется с библиотекой только для заголовков. Измените реализацию какой-либо ключевой функции в библиотеке, и вам может понадобиться перекомпилировать весь проект. Внесите это изменение в исходный файл для скомпилированной библиотеки, и все, что вам нужно сделать, это перекомпилировать один исходный файл библиотеки, обновить скомпилированную библиотеку этим новым файлом .o и заново связать приложение.
Труднее человеку читать. Даже с самой лучшей документацией пользователям библиотеки часто приходится читать заголовки для библиотеки. Заголовки в библиотеке только для заголовков заполнены деталями реализации, которые мешают пониманию интерфейса. В скомпилированной библиотеке все, что вы видите, это интерфейс и краткий комментарий о том, что делает реализация, и это обычно все, что вам нужно. Это действительно все, что вы должны хотеть. Вам не нужно знать детали реализации, чтобы знать, как использовать библиотеку.
Я знаю, что это старый поток, но никто не упомянул интерфейсы ABI или конкретные проблемы компилятора. Я так и думал.
Это в основном основано на концепции того, что вы либо пишете библиотеку с заголовком для раздачи людям, либо повторно используете себя, а не все в заголовке. Если вы думаете о том, чтобы повторно использовать заголовочные и исходные файлы и перекомпилировать их в каждом проекте, тогда это на самом деле не применимо.
В основном, если вы компилируете свой код C ++ и собираете библиотеку с одним компилятором, тогда пользователь пытается использовать эту библиотеку с другим компилятором или другой версией того же компилятора, тогда вы можете получить ошибки компоновщика или странное поведение во время выполнения из-за двоичной несовместимости.
Например, поставщики компиляторов часто меняют свою реализацию STL между версиями. Если у вас есть функция в библиотеке, которая принимает std :: vector, то она ожидает, что байты в этом классе будут расположены так, как они были расположены при компиляции библиотеки. Если в новой версии компилятора поставщик улучшил эффективность std :: vector, тогда код пользователя видит новый класс, который может иметь другую структуру, и передает эту новую структуру в вашу библиотеку. С этого момента все идет вниз … Поэтому не рекомендуется передавать объекты STL через границы библиотек. То же самое относится к типам C Run-Time (CRT).
Говоря о CRT, ваша библиотека и исходный код пользователя обычно должны быть связаны с одним и тем же CRT. В Visual Studio, если вы строите свою библиотеку с использованием многопоточного CRT, но пользователь связывается с многопоточным отладочным CRT, тогда у вас будут проблемы со связью, потому что ваша библиотека может не найти нужные ей символы. Я не могу вспомнить, какая это была функция, но для Visual Studio 2015 Microsoft сделала одну функцию CRT встроенной. Внезапно это было в заголовке, а не в библиотеке CRT, поэтому библиотеки, которые ожидали найти его во время соединения, больше не могли это делать, и это приводило к ошибкам соединения. В результате эти библиотеки нуждались в перекомпиляции с Visual Studio 2015.
Вы также можете получить ошибки связи или странное поведение, если вы используете Windows API, но создаете с другими настройками Unicode для пользователя библиотеки. Это связано с тем, что в API Windows есть функции, использующие строки Unicode или ASCII и макросы /, которые автоматически используют правильные типы на основе настроек Unicode проекта. Если вы передадите строку через границу библиотеки, которая имеет неправильный тип, то все прекратится во время выполнения. Или вы можете обнаружить, что программа не ссылается в первую очередь.
Это также верно для передачи объектов / типов через границы библиотек из сторонних библиотек (например, вектор Eigen или матрица GSL). Если сторонняя библиотека изменит свой заголовок между вами, компилирующим библиотеку, и вашим пользователем, компилирующим их код, то все будет плохо.
По сути, для обеспечения безопасности единственные вещи, которые вы можете передавать через границы библиотек, встроены в типы и Plain Old Data (POD). В идеале любой POD должен быть в структурах, определенных в ваших собственных заголовках, и не полагаться на сторонние заголовки.
Если вы предоставляете библиотеку только с заголовками, тогда весь код компилируется с одинаковыми настройками компилятора и с одинаковыми заголовками, поэтому многие из этих проблем исчезают (если версия третьей, частично используемой вами и вашими пользователями библиотеки совместима с API).
Однако есть негативы, которые были упомянуты выше, такие как увеличенное время компиляции. Кроме того, вы, возможно, управляете бизнесом, поэтому вы можете не захотеть передавать все детали реализации исходного кода всем своим пользователям в случае, если один из них украдет их.
Основным «преимуществом» является то, что он требует от вас доставки исходного кода, поэтому
вы получите отчеты об ошибках на компьютерах и компиляторах
никогда не слышал. Когда библиотека полностью шаблонов, у вас нет
большой выбор, но когда у вас есть выбор, заголовок только плохой
инженерный выбор. (С другой стороны, конечно, заголовок только означает, что
Вам не нужно документировать какие-либо процедуры интеграции.)