Виявлення зайвих # включень в C / C ++?


289

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

Чи є якийсь інструмент, який може виявити зайві директиви #include та підказати, які з них я можу безпечно видалити?
Чи може це робити линзи?


1
Дивіться також: stackoverflow.com/questions/74326/…
Eclipse

1
Зв'язане запитання, здається, вирішує проблему лише в Windows, зокрема, використовуючи Visual Studio.
D'Nabre

7
Голосування за його повторне відкриття, оскільки дублікат стосується конкретного використання Visual Studio.
Дрю Дорманн

Відповіді:


42

Це не автоматично, але доксиген створює діаграми залежності для #includedфайлів. Вам доведеться пройти через них візуально, але вони можуть бути дуже корисними для отримання уявлення про те, що використовується для чого.


5
Це чудовий спосіб побачити ланцюги .. бачачи A -> B -> C -> D і A -> D негайно виявляє надмірність.
Том

34
@Tom: Це жахлива ідея: для одного це не відображає, чи потрібні ці включення чи ні, і по-друге, список включає не повинен залежати від непрямих включень, які можуть змінитися в майбутньому (надлишкові включає, як правило, не такі все-таки велика проблема, завдяки включенню охоронців та магії компілятора), але про те, які класи / функції фактично використовуються у файлі (Вашому компілятору не повинно було проходити тисячі рядків коду шаблону, які навіть не інстанціюються)
MikeMB

@albert, чи можете ви включити знімки цього екрана та коротко описати, де натиснути на вихід доксигену?
Габріель Степлес

@GabrielStaples Це не моя відповідь, тому я не хочу до неї додавати інформацію. Я лише виправив посилання (як місце розміщення, на якому він хотів, зупинився / захопився для використання).
Альберт

177

Google cppclean (посилання на: завантаження , документацію ) може знайти кілька категорій проблем C ++, і тепер він може знайти зайві #includes.

Існує також інструмент на базі Кланг, включте- що-ви використовуєте , який може це зробити. include-what-you-use може навіть запропонувати декларації вперед (тому вам не доведеться #include так багато) та необов'язково очистити #includes для вас.

Поточні версії Eclipse CDT також вбудовані в цю функціональність: перейшовши в меню "Джерело" та натиснувши "Впорядкувати включено", ви будете алфавітувати ваші # включення, додавати заголовки, які Eclipse вважає, що ви використовуєте, не включаючи їх безпосередньо, та коментувати будь-які заголовки, які не мають я не думаю, що потрібно. Однак ця функція не є на 100% надійною.


2
Це робить і зараз. Я тільки починаю його використовувати. Дивіться мою записку тут. stackoverflow.com/questions/1301850/…
шанс

1
Репозиторій cppclean не працює, тепер його можна отримати тут: bitbucket.org/robertmassaioli/cppclean (оригінальний сайт все ще корисний для використання в деяких прикладах)
Нік

3
Я оновив посилання на підтримуваний вилка cppclean
BenC,

1
Зауважте, що здається, що cppclean їх знаходить лише у заголовкових файлах, а не у файлах cpp, з документа: "Не потрібно # включає в файли заголовків".
Zitrax

1
@wizurd - Я не в курсі останніх подій у Eclipse CDT, але я не думаю. iwyu є ретельним і відносно повільним. Аналіз CDT Eclipse - швидкий (інтерактивний) і, коли я його перевіряв, менш точний.
Джош Келлі

65

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


6
IMHO для цієї відповіді потребує набагато більше оновлень, оскільки після опрацювання перешкод інструмент IWYU Google стане остаточним інструментом для виконання цього завдання.
Ден Олсон

5
sudo apt-get install iwyu
Andrew Wagner

Здається чудово - з двома кавалетами 1) останнє оновлення лютого 2106 2) Gogole самі використовують його лише для C ++, а не для C, про що вимагала ОП.
Мавг каже, що повернути Моніку

Чи можете ви пояснити трохи, як користувач повинен ним користуватися? У README не дуже зрозуміло, що містить вихід сценарію python.
королівський джестер

Я використовую це, але це не завжди на 100% правильно. Можливо, 70% разів це дає правильні пропозиції.
Допитливий

25

Проблема з виявленням зайвого включає те, що воно не може бути просто перевіряючою залежністю типу. Не зайвим є файл, який не забезпечує нічого компіляційному і не змінює інший елемент, від якого залежать інші файли. Існує багато способів, як заголовок-файл може змінити компіляцію, скажімо, визначивши константу, переосмисливши та / або видаливши використаний макрос, додавши простір імен, який певним чином змінює пошук імені. Для виявлення таких елементів, як простір імен, вам потрібно набагато більше, ніж препроцесору, вам фактично майже потрібен повний компілятор.

Lint - це більше перевірка стилю, і, звичайно, не матиме такої повної можливості.

Я думаю, що ви знайдете єдиний спосіб виявити зайву функцію - це видалити, компілювати та запустити набори.


8
Нічого з цього не буде проблемою, якщо файли включають добре викладені. Якщо вам коли-небудь потрібно включити файл A до файлу B, ви робите це неправильно (і я працював над проектами, де вони зробили неправильно).
Девід Торнлі

9
@David, так, але це залежить від років дияволів, перш ніж робити це правильно. Я можу з великою впевненістю сказати, що шанси на те, що відбувається,
приносять

Так, але я, як правило, про це дізнаюся під час зміни програми, і раптом у мене з’являється помилка компіляції (якщо мені пощастило) або незрозуміла помилка. Це, здається, зберігає файли #include чесними, принаймні, в довгостроковій перспективі.
Девід Торнлі

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

1
@ Benoit, тоді ви б ігнорували клас питань, які складаються, але семантично змінюють значення вашої програми. Поміркуйте, як #define в одному файлі може змінити гілку #if в іншому. Видалення заголовка все ще може дозволити компіляції з різними результатами
JaredPar

15

Я думав, що PCLint зробить це, але минуло кілька років, як я це переглянув. Ви можете це перевірити.

Я переглянув цей блог, і автор трохи поговорив про налаштування PCLint для пошуку невикористаних включень. Можливо, варто подивитися.


Гарна знахідка! Мені доведеться цим скористатися.
crashmstr

4
Я регулярно використовую PCLint, і це говорить про невикористані заголовки. Я обережно коментую заголовок #include та перекомпілюйте, щоб бути впевненим, що заголовок справді не використовується ...
Harold Bamford,

Дякую за підтвердження, Гарольде.
itsmatt

5
занадто дорого. не є життєздатним інструментом для мас.

7

CScout рефакторінга браузер може виявити зайве включати директиви в C ( до жаль , НЕ C ++) коду. Опис того, як це працює, ви можете знайти в цій статті журналу.


5

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

Нехай воно працює протягом ночі, а наступного дня у вас буде 100% правильний список файлів, що включають файли, які ви можете видалити.

Іноді груба сила просто працює :-)


редагувати: а іноді це не так :-). Ось трохи інформації з коментарів:

  1. Іноді ви можете видалити два файли заголовка окремо, але не обидва разом. Рішення - видалити файли заголовків під час запуску, а не повертати їх назад. Тут знайдеться список файлів, які ви можете безпечно видалити, хоча може бути рішення з більшою кількістю файлів, видалити які цей алгоритм не знайде. (це жадібний пошук по простору включених файлів, які потрібно видалити. Він знайде лише локальний максимум)
  2. Можливі незначні зміни в поведінці, якщо у вас є деякі макроси, переосмислені по-різному, залежно від деяких #ifdefs. Я думаю, що це дуже рідкісні випадки, і Тестові модулі, які є частиною збірки, повинні сприймати ці зміни.

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

Можливо, це те, що ви мали на увазі, але сценарій, який видаляє єдине включення, а останній видалений включає, якщо він був успішно видалений, зробив би трюк.
Домінік Роджер

1
Погана ідея. Якщо файл заголовка #defines константа BLAH, а інший файл заголовка перевіряє #ifdef BLAH, видалення першого заголовкового файлу все ще може бути успішно скомпільовано, але ваша поведінка змінилася.
Graeme Perrow

1
Це також може спричинити проблеми із заголовками системи, оскільки в різних реалізаціях можуть бути різні речі, включені в #include <vector>. Навіть якщо ви дотримуєтесь одного компілятора, заголовки можуть змінюватися у різних версіях.
Девід Торнлі

2
У цьому випадку не знайдеться випадків, коли ви включаєте заголовок, який містить заголовок, який вам дійсно потрібен.
bk1e

5

Вибачте за (повторну) публікацію тут, люди часто не розширюють коментарі.

Перевірте мій коментар до crashmstr, FlexeLint / PC-Lint зробить це за вас. Інформаційне повідомлення 766. Розділ 11.8.1 мого посібника (версія 8.0) обговорює це.

Крім того, і це важливо, продовжуйте повторювати, поки повідомлення не зникне . Іншими словами, після видалення невикористаних заголовків, повторного запуску вказівника, після видалення деяких непотрібних заголовків, можливо, більше заголовкових файлів можуть стати "непотрібними". (Це може звучати нерозумно, читайте його повільно і аналізуйте, це має сенс.)


Я точно знаю, що ви маєте на увазі, і моя реакція була "Ewwww". Я ненавиджу такий код.
Девід Торнлі

5

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


4

Я спробував використовувати Flexelint (unix версія PC-Lint) і мав дещо неоднозначні результати. Це, ймовірно, тому, що я працюю над дуже великою і вузловою базою коду. Я рекомендую уважно вивчити кожен файл, який повідомляється як невикористаний.

Основна стурбованість - помилкові позитиви. Про кілька заголовків одного заголовка повідомляється як непотрібний заголовок. Це погано, оскільки Flexelint не повідомляє вам, у який рядок включений заголовок або де він був включений раніше.

Один із способів автоматизованих інструментів може помилитися з цим:

В A.hpp:

class A { 
  // ...
};

В B.hpp:

#include "A.hpp

class B {
    public:
        A foo;
};

У C.cpp:

#include "C.hpp"  

#include "B.hpp"  // <-- Unneeded, but lint reports it as needed
#include "A.hpp"  // <-- Needed, but lint reports it as unneeded

Якщо ви сліпо слідкуєте за повідомленнями Flexelint, ви примрусите свої залежності #include. Існує більше патологічних випадків, але в основному вам потрібно буде самостійно оглянути заголовки для найкращих результатів.

Настійно рекомендую цю статтю про фізичну структуру та C ++ з блогу Ігри зсередини. Вони рекомендують комплексний підхід до очищення безладу #include:

Керівні принципи

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

  1. Кожен файл cpp спочатку включає власний файл заголовка. [сніп]
  2. Файл заголовка повинен містити всі файли заголовка, необхідні для його розбору. [сніп]
  3. Файл заголовка повинен мати мінімальну кількість файлів заголовка, необхідних для його розбору. [сніп]

Книга Лакоса чудово підходить для освіти - окрім його застарілих спостережень за технологією компіляції.
Том

4

Якщо ви використовуєте Eclipse CDT, ви можете спробувати http://includator.com, який є безкоштовним для бета-тестерів (на момент написання цього тексту) і автоматично видаляє зайві #includes або додає відсутні. Для тих користувачів, які мають FlexeLint або PC-Lint та використовують Elicpse CDT, http://linticator.com може бути варіантом (також безкоштовним для бета-тесту). Хоча він використовує аналіз Лінта, він забезпечує швидкі виправлення для автоматичного видалення зайвих операторів #include.


Причиною цього є те, що наш відділ бухгалтерії не в змозі виставляти менші суми. Якщо ви рахуєте час, який можете заощадити, це не так нерозумно. Як тільки ми отримали можливість отримувати платежі за допомогою кредитної картки, ми можемо значно знизити ціну. Інший варіант буде спонсором наших зусиль з розвитку. Наша модель фінансування вимагає від нас отримання прибутку для фінансування науково-дослідної роботи. Я був би радий продавати ліцензії набагато дешевше, але не можу. Можливо, ми внесимо його в CDT, і ви отримаєте його безкоштовно, але це я повинен якось фінансувати. Я забув, ви можете спробувати безкоштовно!
PeterSom

2

У цій статті пояснюється техніка видалення #include за допомогою аналізу синтаксису Doxygen. Це просто сценарій perl, тому він досить простий у використанні.


1
Сценарій виявляє, що включає деякі елементи для видалення, але він також надає безліч включень, які неможливо видалити. Здається, що він не підтримує enum класу, схоже, що він поганий час з макросом, а іноді і з простором імен.
Батист Віхт

1

Можливо, трохи пізно, але я одного разу знайшов сценарій perl WebKit, який робив саме те, що ви хотіли. Я потребую певної адаптації, я вважаю (я не дуже добре розбираюся в perl), але він повинен зробити трюк:

http://trac.webkit.org/browser/branches/old/safari-3-2-branch/WebKitTools/Scripts/find-extra-includes

(це стара гілка, оскільки в магістралі більше немає файлу)



1

Існує два типи зайвих файлів #include:

  1. Файл заголовка фактично не потрібен модулю (.c, .cpp)
  2. Заголовок потрібен для модуля, але він включається не раз, прямо чи опосередковано.

На моєму досвіді є два способи, які добре виявляють це:

  • gcc -H або cl.exe / showincludes (вирішити проблему 2)

    У реальному світі ви можете експортувати CFLAGS = -H перед внесенням, якщо всі Makefile не перекривають параметри CFLAGS. Або, як я використав, ви можете створити обгортку cc / g ++, щоб примусово додавати параметри -H до кожного виклику $ (CC) та $ (CXX). і додайте до папки обгортки змінну $ PATH, тоді ваша марка використовує замість вас команду обгортки. Звичайно, ваша обгортка повинна викликати справжній компілятор gcc. Ці трюки потрібно змінити, якщо ваш Makefile використовує gcc безпосередньо. замість $ (CC) або $ (CXX) або за маючи на увазі правила.

    Ви також можете скласти один файл, налаштувавши командний рядок. Але якщо ви хочете очистити заголовки для всього проекту. Ви можете зафіксувати весь результат:

    очистити

    скласти 2> & 1 | tee результат.txt

  • PC-Lint / FlexeLint (вирішити проблему як 1, так і 2)

    не забудьте додати параметри + e766, це попередження про: невикористані файли заголовків.

    pclint / flint -vf ...

    Це призведе до виводу файлу заголовка, що включає в себе pclint, а вкладені файли заголовків будуть відповідним відступом.


1

Закінчивши це обговорення: препроцесор c ++ закінчується. Це смислова властивість, незалежно від того, включити включення. Звідси випливає з теореми Райса, що не можна визначити, зайвим є чи включати. НЕ МОЖЕ бути програми, яка (завжди правильно) визначає, чи не включити включення.


5
Я просив "завжди правильного" рішення? Ця відповідь не дуже результативна для дискусії.
shoosh

1
Ну, було чимало дописів, які обговорювали проблеми, з якими доведеться вирішувати таку програму. Мій пост дає переконливу і правильну відповідь на цю частину дискусії. І я нікому не сподобався б, якби програма мені сказала, я міг би спокійно видалити #include, і тоді мій код більше не компілюється. (або ще гірше - все ще компілюється, але робить щось інакше). БУДЬ-яка така програма несе цей ризик.
Алгоман

4
Між усіма ВИДАМИ про те, наскільки важко це було б, і як ви МОЖЕТЕ вирішити ту чи іншу перешкоду, я дав вам єдину 100% правильну відповідь. Мені здається досить сміливим сказати, що це не було результативним ...
Алгоман

1
Я згадав, що теорема Райса говорить: "Не може бути програми, яка завжди може перевірити, чи задана програма вирішує цю зайву проблему". Існує декілька програм, які вирішують проблему із зайвим включенням.
Чже Ян

1
особисто я вважав, що введення @ Алгомана дуже корисне. змушує мене усвідомити, наскільки важка ця проблема.
богардон


0

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

Редагувати: Це може. Дивіться відповідь їїматта


Ви впевнені в цьому? Я не використовував FlexeLint (такий же, як PCL) протягом декількох років на C ++-коді, але навіть нещодавно на C-коді я можу поклястись, що побачив пару повідомлень (я думаю, це код 766?) Про невикористані файли заголовків. Щойно перевірено (v8.0), див. Розділ 11.8.1. посібника.
День

0

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

Я виявив, що ви платите за цю функціональність; CLion потребує певного часу, щоб сканувати та аналізувати проект при першому завантаженні.

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