Переваги бібліотек лише із заголовками


98

Які переваги має лише бібліотека заголовків, і чому ви пишете її таким чином, щоб не покласти реалізацію в окремий файл?


Переважно шаблони, але це також полегшить розповсюдження та використання.
BoBTFish

4
Я хотів би додати недоліки бібліотеки, що має лише заголовки, до обсягу питання ...
moooeeeep

Які мінуси є, про які вже не згадувалося?
NebulaFox

7
@moooeeeep: з мінусів, можливо, ви захочете прочитати параграф "Зупинити вбудовування коду" на веб-сторінці C ++ Dos і Don'ts Chromium Projects.
Mr.C64,

Відповіді:


57

Бувають ситуації, коли єдиним варіантом є лише бібліотека заголовків, наприклад, коли йдеться про шаблони.

Наявність бібліотеки лише для заголовків також означає, що вам не доведеться турбуватися про різні платформи, де бібліотека може використовуватися. Коли ви відокремлюєте реалізацію, ви зазвичай робите це, щоб приховати деталі реалізації та розподілити бібліотеку як комбінацію заголовків та бібліотек ( lib, dllабо.so файлів файлів). Вони, звичайно, повинні бути скомпільовані для всіх різних операційних систем / версій, які ви пропонуєте.

Ви також можете розповсюджувати файли реалізації, але це означатиме додатковий крок для користувача - складання вашої бібліотеки перед її використанням.

Звичайно, це стосується кожного конкретного випадку . Наприклад, іноді збільшуються бібліотеки лише із заголовкамирозмір коду & час складання.


6
"Наявність бібліотеки лише для заголовків також означає, що вам не доведеться турбуватися про різні платформи, де бібліотека може використовуватися": лише якщо вам не потрібно підтримувати бібліотеку. В іншому випадку це кошмар, повідомляючи про помилки, які ви не можете відтворити або перевірити на матеріалі, який у вас є.
James Kanze,

1
Я щойно задав подібне запитання щодо переваг продуктивності лише заголовка. Як бачите, різниці в розмірі коду немає. Однак приклад реалізації лише заголовка працював на 7% повільніше. stackoverflow.com/questions/12290639 / ...
Homer6

@ Homer6 дякую за те, що ти мені написав. Я насправді ніколи цього не вимірював.
Luchian Grigore

1
@LuchianGrigore Я не міг знайти нікого іншого, хто б мав. Тому відповідь знадобився деякий час. Є так багато спекулятивних коментарів щодо "збільшення розміру коду" та "споживання пам'яті". Нарешті я отримав знімок відмінностей, навіть якщо це лише один приклад.
Гомер, 6

@ Гомер6. Чому б не збільшити розмір коду? Якщо припустити, що ви створюєте декілька бібліотек, які використовують лише заголовок lib, а потім ваш додаток використовує всі ті бібліотеки, які ви мали б мати кілька копій, на відміну від посилань на одну спільну бібліотеку.
pooya13

60

Переваги бібліотеки лише для заголовків:

  • Спрощує процес складання. Вам не потрібно будувати бібліотеку, і вам не потрібно вказувати скомпільовану бібліотеку під час кроку зв’язку збірки. Якщо у вас є скомпільована бібліотека, ви, мабуть, захочете створити кілька її версій: одну, складену з увімкненою налагодженням, іншу з увімкненою оптимізацією і, можливо, ще одну позбавлену символів. І, можливо, навіть більше для мультиплатформної системи.

Недоліки бібліотеки лише для заголовків:

  • Більші файли об’єктів. Кожен вбудований метод із бібліотеки, який використовується в якомусь вихідному файлі, також отримає слабкий символ, поза рядком визначення у складеному об'єктному файлі для цього вихідного файлу. Це уповільнює компілятор, а також уповільнює компоновщик. Компілятор повинен генерувати все, що роздувається, а потім компонувальник повинен це відфільтрувати.

  • Більш довга компіляція. На додаток до проблеми здуття, згаданої вище, компіляція триватиме довше, оскільки заголовки за своєю суттю більші, ніж у бібліотеки лише заголовків, ніж у скомпільованої бібліотеки. Ці великі заголовки потрібно буде проаналізувати для кожного вихідного файлу, який використовує бібліотеку. Інший фактор полягає в тому, що ці файли заголовків у бібліотеці, що містить лише #includeзаголовки, повинні мати заголовки, необхідні для вбудованих визначень, а також заголовки, які були б потрібні, якби бібліотека була побудована як скомпільована бібліотека.

  • Більш заплутана компіляція. Ви отримуєте набагато більше залежностей з бібліотекою лише заголовків, оскільки додаткові #includes потрібні для бібліотеки лише заголовків. Змініть реалізацію якоїсь ключової функції в бібліотеці, і вам може знадобитися перекомпілювати весь проект. Внесіть зміни у вихідний файл для скомпільованої бібліотеки, і все, що вам потрібно зробити, - це перекомпілювати цей вихідний файл бібліотеки, оновити скомпільовану бібліотеку цим новим файлом .o та знову зв’язати програму.

  • Людині важче читати. Навіть маючи найкращу документацію, користувачам бібліотеки часто доводиться вдаватися до читання заголовків бібліотеки. Заголовки в бібліотеці, що містить лише заголовки, заповнені деталями реалізації, які заважають зрозуміти інтерфейс. Завдяки скомпільованій бібліотеці все, що ви бачите, - це інтерфейс та короткий коментар щодо того, що робить реалізація, і це, як правило, все, що вам потрібно. Це насправді все, що ви повинні хотіти. Вам не потрібно знати деталі реалізації, щоб знати, як користуватися бібліотекою.


21
Останній пункт насправді не має сенсу. Будь-яка обґрунтована документація включатиме декларацію функції, параметри, повернені значення тощо .. та всі пов’язані коментарі. Якщо вам доведеться звернутися до заголовного файлу, документація не вдалася.
Томас

6
@Thomas - Навіть маючи найкращі професійні бібліотеки, мені часто доводиться вдаватися до читання "чудового" заголовка. Насправді, якщо з коду та коментарів витягується так звана "чудова" документація, я, як правило, люблю читати заголовки. Код та коментарі говорять мені більше, ніж автоматично згенерована документація.
Девід Хаммен,

2
Останній пункт не дійсний. Заголовки вже заповнені деталями реалізації в приватних членах, тому це не так, як файл cpp приховує всі деталі реалізації. Крім того, такі мови, як C #, за своєю суттю є "лише заголовком", а IDE піклується про затемнення деталей ("складає" їх)
Марк Лаката,

2
@Tomas: Погодьтеся, останній пункт абсолютно фіктивний. Ви можете легко зберігати інтерфейс та реалізацію настільки ж окремо, як і з бібліотеками лише для заголовків; у вас просто є заголовок інтерфейсу # включити деталі реалізації. Ось чому бібліотеки Boost зазвичай містять підкаталог (та простір імен) detail.
Немо

4
@Thomas: Я не згоден. Заголовочний файл - це, як правило, перше місце, куди я звертаюся за документацією. Якщо заголовок добре написаний, часто немає потреби у зовнішній документації.
Джоел Корнетт,

14

Я знаю, що це стара тема, але ніхто не згадував про інтерфейси ABI чи конкретні проблеми компілятора. Тож я думав, що буду.

В основі цього лежить концепція того, що ви або пишете бібліотеку із заголовком для розповсюдження людям, або повторно використовуєте себе проти того, щоб мати все в заголовку. Якщо ви думаєте повторно використовувати заголовок та вихідні файли та перекомпілювати їх у кожному проекті, це насправді не стосується.

В основному, якщо ви компілюєте свій код C ++ і створюєте бібліотеку з одним компілятором, тоді користувач намагається використовувати цю бібліотеку з іншим компілятором або іншою версією того самого компілятора, тоді ви можете отримати помилки компонувальника або дивну поведінку під час виконання через бінарну несумісність.

Наприклад, постачальники компіляторів часто змінюють реалізацію STL між версіями. Якщо у вас є функція в бібліотеці, яка приймає std :: vector, то вона очікує, що байти в цьому класі будуть розташовані так, як вони були розташовані під час компіляції бібліотеки. Якщо в новій версії компілятора постачальник покращив ефективність std :: vector, тоді код користувача бачить новий клас, який може мати іншу структуру, і передає цю нову структуру у вашу бібліотеку. Звідти все йде вниз ... Ось чому рекомендується не передавати об’єкти STL через межі бібліотеки. Те саме стосується типів часу виконання C (CRT).

Розмовляючи про ЕПТ, ваша бібліотека та вихідний код користувача, як правило, повинні бути пов'язані з однією і тією ж ЕПТ. З Visual Studio, якщо ви створюєте свою бібліотеку за допомогою багатопотокової ЕПТ, але користувач посилається на багатопотокову налагоджувальну ЕПТ, у вас виникнуть проблеми із посиланнями, оскільки ваша бібліотека може не знайти потрібні символи. Я не пам’ятаю, яка це була функція, але для Visual Studio 2015 Microsoft зробила одну функцію ЕПТ вбудованою. Раптом це було в заголовку, а не в бібліотеці ЕПТ, тому бібліотеки, які очікували знайти його під час зв’язку, більше не могли робити, і це спричинило помилки при зв’язку. Результатом стало те, що ці бібліотеки потребували перекомпіляції з Visual Studio 2015.

Ви також можете отримати помилки посилання або дивну поведінку, якщо ви використовуєте Windows API, але будуєте з різними налаштуваннями Unicode для користувача бібліотеки. Це пояснюється тим, що API Windows має функції, які використовують або рядки Unicode, або ASCII, і макроси / визначає, які автоматично використовують правильні типи на основі параметрів Unicode проекту. Якщо ви передаете рядок через межі бібліотеки неправильного типу, то речі порушуються під час виконання. Або ви можете виявити, що програма не пов’язує спочатку.

Це також стосується передачі об’єктів / типів через межі бібліотек від інших сторонніх бібліотек (наприклад, власний вектор або матриця GSL). Якщо стороння бібліотека змінить заголовок між компіляцією бібліотеки та користувачем, який компілює код, тоді все буде зламано.

В основному для безпеки єдині речі, які ви можете передати через межі бібліотеки, вбудовані в типи та звичайні старі дані (POD). В ідеалі будь-який POD повинен мати структури, визначені у ваших власних заголовках, і не покладатися на жодні сторонні заголовки.

Якщо ви надаєте бібліотеку лише для заголовків, тоді весь код компілюється з однаковими налаштуваннями компілятора та проти тих самих заголовків, тому багато цих проблем зникає (надання версії третіх частково бібліотек, які ви та ваш користувач використовуєте, сумісні з API).

Однак є негативи, які були згадані вище, такі як збільшений час складання. Крім того, ви можете керувати бізнесом, тож ви можете не захотіти передавати всі деталі реалізації вихідного коду всім своїм користувачам на випадок, якщо хтось із них викраде його.


8

Головна "перевага" полягає в тому, що для цього потрібно надати вихідний код, тож ви отримаєте звіти про помилки на машинах та компілятори, про які ви ніколи не чули. Коли бібліотека - це цілком шаблони, у вас немає великого вибору, але коли у вас є вибір, лише заголовок, як правило, є поганим технічним вибором. (З іншого боку, звичайно, заголовок означає лише те, що вам не потрібно документувати жодну процедуру інтеграції.)


0

Вбудовування можна здійснити за допомогою оптимізації часу зв'язку (LTO)

Я хотів би це підкреслити, оскільки це зменшує значення однієї з двох основних переваг бібліотек лише для заголовків: "вам потрібні визначення в заголовку для вбудовування".

Мінімальний конкретний приклад цього наведено у: Оптимізація часу зв'язку та вбудована

Отже, ви просто передаєте прапор, і вбудовування можна виконувати в об’єктних файлах без будь-якої роботи з рефакторингу, і для цього більше не потрібно зберігати визначення в заголовках.

Однак LTO може мати і свої мінуси: чи є причина, чому не використовувати оптимізацію часу зв'язку (LTO)?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.