Обнаружение лишних #include в C / C ++?

289

Я часто нахожу, что раздел заголовков файла постоянно увеличивается и увеличивается, но он никогда не уменьшается. В течение жизни исходного файла классы могли перемещаться и подвергаться рефакторингу, и вполне возможно, что есть немало тех, #includesкоторые не должны быть там и больше. Оставив их там, можно только продлить время компиляции и добавить ненужные зависимости компиляции. Попытка выяснить, что еще нужно, может быть довольно утомительной.

Есть ли какой-нибудь инструмент, который может обнаружить лишние директивы #include и предложить, какие из них я могу безопасно удалить?
Может ли это сделать Линт?

shoosh
источник
1
См. Также: stackoverflow.com/questions/74326/…
Затмение
1
Похоже, что связанный вопрос решает проблему только в Windows, в частности с использованием Visual Studio.
Д'Набр
7
Голосование, чтобы вновь открыть это, поскольку дубликат об использовании Visual Studio, в частности.
Дрю Дорманн

Ответы:

42

Это не автоматически, но doxygen создаст диаграммы зависимостей для #includedфайлов. Вам придется просматривать их визуально, но они могут быть очень полезны для получения изображения того, что и что использует.

альберт
источник
5
Это отличный способ увидеть цепочки .. видя, что A -> B -> C -> D и A -> D сразу обнаруживает избыточность.
Том
34
@Tom: Это ужасная идея: для одного не видно, нужны ли эти включения, или, во-вторых, список включений не должен зависеть от косвенных включений, которые могут измениться в будущем (избыточные включения обычно не являются такими В любом случае, это большая проблема, благодаря включению защиты и магии компилятора), но какие классы / функции фактически используются в файле (ваш компилятор не должен проходить тысячи строк кода шаблона, которые даже не создаются)
MikeMB
@albert, можете ли вы включить скриншоты этого и кратко описать, где нажать в выводе кислорода?
Габриэль Стейплс
@GabrielStaples Это не мой ответ, поэтому я не хочу добавлять к нему информацию. Я только исправил ссылку (поскольку упомянутое место размещения остановлено / изъято для использования).
Альберт
177

Google cppclean (ссылки на: скачать , документация ) может найти несколько категорий проблем C ++, и теперь он может найти лишние #include.

Есть также инструмент на основе Clang, include-what-you-use , который может это сделать. include-what-you-use может даже предложить предварительные объявления (так что вам не нужно так много включать #include) и при желании очистить ваши #include для вас.

В текущие версии Eclipse CDT также встроена эта функциональность: перейдя в меню «Источник» и нажав «Организовать включения», вы расположите в алфавитном порядке ваши # include, добавьте любые заголовки, которые, по мнению Eclipse, вы используете, не включая их непосредственно, и закомментируйте любые заголовки, которые у него нет. не думаю, что вам нужно. Однако эта функция не на 100% надежна.

Джош Келли
источник
2
Это сейчас. Я только начинаю его использовать. Смотрите мою заметку здесь. stackoverflow.com/questions/1301850/...
шанс
1
Репозиторий cppclean не работает, теперь вы можете получить его здесь: bitbucket.org/robertmassaioli/cppclean ( хотя оригинальный сайт все еще полезен для некоторых примеров использования)
Ник
3
Я обновил ссылку на поддерживаемый форк cppclean: github.com/myint/cppclean
BenC
1
Обратите внимание, что cppclean, кажется, находит их только в заголовочных файлах, а не в файлах cpp из документа: «Ненужное #include в заголовочных файлах».
Цитракс
1
@wizurd - я не следил за последними разработками в Eclipse CDT, но я так не думаю. Iwyu тщательно и относительно медленно. Анализ Eclipse CDT быстрый (интерактивный) и, когда я его тестировал, менее точный.
Джош Келли
65

Также проверьте include-what-you-use , который решает похожую проблему.

Tzafrir
источник
6
ИМХО, этот ответ нуждается в гораздо большем количестве голосов, так как после того, как изломы решены, инструмент IWYU от Google станет окончательным инструментом для этой задачи.
Дэн Олсон,
5
sudo apt-get install iwyu
Эндрю Вагнер
Кажется великолепным - с двумя каваетами 1) последнее обновление февраль 2106 2) Сами Гоголе используют его только для C ++, а не для C, который запрашивал OP.
Mawg говорит восстановить Монику
Можете ли вы объяснить немного, как пользователь должен использовать его? В README не очень ясно, что содержит вывод скрипта python.
Король шут
Я использую это, но это не всегда 100% правильно. Может быть, 70% раз это дает правильные предложения.
InQusitive
25

Проблема с обнаружением лишних включений заключается в том, что это не может быть просто проверка зависимости типа. Лишний include - это файл, который не дает ничего полезного для компиляции и не изменяет другой элемент, от которого зависят другие файлы. Существует много способов, которыми файл заголовка может изменить компиляцию, например, путем определения константы, переопределения и / или удаления использованного макроса, добавления пространства имен, которое изменяет поиск имени каким-либо образом вниз по строке. Чтобы обнаружить такие элементы, как пространство имен, вам нужно гораздо больше, чем препроцессор, фактически вам нужен почти полный компилятор.

Lint - это скорее средство проверки стиля, и, конечно, у него не будет такой полной возможности.

Я думаю, вы найдете единственный способ обнаружить лишнее включение - удалить, скомпилировать и запустить пакеты.

JaredPar
источник
8
Ничего из этого не будет проблемой, если включаемые файлы выложены правильно. Если вам когда-нибудь понадобится включить файл A перед файлом B, вы делаете это неправильно (и я работал над проектами, где они делали это неправильно).
Дэвид Торнли
9
@ Дэвид, да, но это зависит от лет разработчиков, прежде чем ты сделаешь это правильно. Я могу с большой уверенностью сказать, что вероятность того, что это
произойдет,
Да, но я обычно узнаю об этом при изменении программы, и внезапно у меня возникает ошибка компиляции (если мне повезет) или неясная ошибка. Это, похоже, делает файлы #include честными, по крайней мере, в долгосрочной перспективе.
Дэвид Торнли
Я бы сказал прямо противоположное. Все, что вам нужно, это проверка зависимости типа. Он может не скомпилироваться после того, как вы упорядочили включения соответствующим образом, но это проблемы, которые должны быть решены в любом случае.
Benoît
1
@Benoit, тогда вы будете игнорировать класс проблем, которые компилируются, но семантически меняют смысл вашей программы. Подумайте, как #define в одном файле может изменить ветку #if в другом. Удаление заголовка может все еще позволить этому скомпилировать с другими результатами
JaredPar
15

Я думал, что PCLint сделает это, но прошло несколько лет с тех пор, как я на это посмотрел. Вы можете проверить это.

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

itsmatt
источник
Хорошая находка! Я должен использовать это.
crashmstr
4
Я регулярно использую PCLint, и он говорит мне о неиспользованных заголовках. Я осторожно закомментирую заголовок #include и перекомпилирую, чтобы убедиться, что заголовок действительно не используется ...
Гарольд Бамфорд
Спасибо за подтверждение, Гарольд.
itmatt
5
слишком дорогой. не жизнеспособный инструмент для масс.
7

CScout рефакторинга браузер может обнаружить лишнее включать директивы в C ( к сожалению , не C ++) кода. Вы можете найти описание того, как это работает, в этой журнальной статье.

Диомидис Спинеллис
источник
5

Вы можете написать быстрый скрипт, который удаляет одну директиву #include, компилирует проекты и записывает имя в #include и файл, из которого он был удален, в случае, если не возникло ошибок компиляции.

Позвольте ему работать ночью, и на следующий день у вас будет 100% правильный список включаемых файлов, которые вы можете удалить.

Иногда грубой силы просто работает :-)


редактировать: и иногда это не так :-). Вот немного информации из комментариев:

  1. Иногда вы можете удалить два заголовочных файла по отдельности, но не оба вместе. Решение состоит в том, чтобы удалить заголовочные файлы во время выполнения, а не вернуть их обратно. Это позволит найти список файлов, которые вы можете безопасно удалить, хотя может быть решение с большим количеством файлов для удаления, которое этот алгоритм не найдет. (это жадный поиск по пространству включаемых файлов для удаления. Он найдет только локальный максимум)
  2. В поведении могут быть небольшие изменения, если некоторые макросы переопределяются по-разному в зависимости от некоторых #ifdefs. Я думаю, что это очень редкие случаи, и модульные тесты, являющиеся частью сборки, должны уловить эти изменения.
Гилад Наор
источник
1
Будьте осторожны с этим - скажем, есть два заголовочных файла, которые оба содержат определение чего-либо. Вы можете удалить либо, но не оба. Вам нужно быть немного более тщательным в своем подходе грубой силы.
Доминик Роджер
Возможно, это то, что вы имели в виду, но сценарий, который удаляет одно включение и оставляет последнее удаленное включение, если оно было успешно удалено, сработает.
Доминик Роджер
1
Плохая идея. Если файл заголовка #define определяет константу BLAH, а другой файл заголовка проверяет #ifdef BLAH, удаление первого файла заголовка может все еще успешно скомпилироваться, но ваше поведение изменилось.
Грэм Перроу
1
Это также может вызвать проблемы с системными заголовками, так как разные реализации могут включать разные вещи в #include <vector>. Даже если вы придерживаетесь одного компилятора, заголовки могут меняться в зависимости от версии.
Дэвид Торнли
2
Это не найдет случаев, когда вы включаете заголовок, который включает заголовок, который вам действительно нужен.
bk1e
5

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

Посмотрите мой комментарий к crashmstr, FlexeLint / PC-Lint сделает это за вас. Информационное сообщение 766. Это обсуждается в разделе 11.8.1 моего руководства (версия 8.0).

Кроме того, и это важно, продолжайте итерацию, пока сообщение не исчезнет . Другими словами, после удаления неиспользуемых заголовков, повторно запустите lint, больше файлов заголовков могло бы стать "ненужным", как только вы удалите некоторые ненужные заголовки. (Это может звучать глупо, читать медленно и разбирать, это имеет смысл.)

Дэн
источник
Я точно знаю, что вы имеете в виду, и моя реакция была "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 ++ из блога Games from inside. Они рекомендуют комплексный подход к устранению путаницы #include:

Руководящие указания

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

  1. Каждый файл cpp сначала включает свой собственный заголовочный файл. [Надрез]
  2. Заголовочный файл должен включать все заголовочные файлы, необходимые для его анализа. [Надрез]
  3. Заголовочный файл должен иметь минимальное количество заголовочных файлов, необходимых для его анализа. [Надрез]
Бен Мартин
источник
Книга Лакоса отлично подходит для образования, если не считать его устаревших наблюдений за технологиями компиляции.
Том
4

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

PeterSom
источник
Причина в том, что наш бухгалтерский отдел не может выставлять меньшие суммы. Если вы посчитаете время, которое вы можете сэкономить, это не так уж и необоснованно. После того, как мы получили возможность принимать платежи по кредитным картам, мы можем значительно снизить цену. Другой вариант будет спонсором наших усилий по развитию. Наша модель финансирования требует от нас получения прибыли для финансирования нашей исследовательской работы. Я был бы рад продать лицензии намного дешевле, но не могу. Может быть, мы внесем это в CDT, и вы получите это бесплатно, но это я должен как-то финансировать. Я забыл, вы можете попробовать бесплатно!
PeterSom
2

В этой статье описывается метод удаления #include с помощью анализа Doxygen. Это всего лишь скрипт на Perl, поэтому его довольно просто использовать.

Стив Гури
источник
1
Сценарий находит некоторые включения для удаления, но он также дает множество включений, которые нельзя удалить. Кажется, что он не поддерживает перечисление классов, кажется, что он плохо работает с макросами, а иногда и с пространством имен.
Креститель
1

Может быть, немного поздно, но однажды я нашел Perl-скрипт WebKit, который делал то, что вы хотели. Я думаю, что потребуется некоторая адаптация (я не очень хорошо разбираюсь в Perl), но это должно сработать:

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

(это старая ветка, потому что в стволе больше нет файла)

rubenvb
источник
1

Существует два типа лишних файлов #include:

  1. Файл заголовка вообще не нужен модулю (.c, .cpp)
  2. Файл заголовка необходим модулю, но включается более одного раза, прямо или косвенно.

В моем опыте есть 2 способа, которые хорошо подходят для его обнаружения:

  • gcc -H или cl.exe / showinclude (решить проблему 2)

    В реальном мире вы можете экспортировать CFLAGS = -H перед make, если все Makefile не переопределяют параметры CFLAGS. Или, как я использовал, вы можете создать оболочку cc / g ++, чтобы принудительно добавлять опции -H к каждому вызову $ (CC) и $ (CXX). и добавьте в каталог оболочки переменную $ PATH, тогда ваш make будет использовать вместо вас вашу команду оболочки. Конечно, ваша обертка должна вызывать настоящий компилятор gcc. Этот трюк нужно изменить, если ваш Makefile напрямую использует gcc. вместо $ (CC) или $ (CXX) или по подразумеваемым правилам.

    Вы также можете скомпилировать один файл, настроив его из командной строки. Но если вы хотите очистить заголовки для всего проекта. Вы можете захватить весь вывод:

    очистить

    сделать 2> & 1 | tee result.txt

  • PC-Lint / FlexeLint (решить проблему 1 и 2)

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

    pclint / flint -vf ...

    Это приведет к выводу pclint включенных заголовочных файлов, вложенные заголовочные файлы будут иметь соответствующие отступы.

zhaorufei
источник
1

Чтобы закончить это обсуждение: препроцессор c ++ завершен. Это семантическое свойство, является ли включение избыточным. Следовательно, из теоремы Райс следует, что неразрешимо, является ли включение избыточным или нет. НЕ МОЖЕТ быть программа, которая (всегда правильно) определяет, является ли включение лишним.

Algoman
источник
5
Я попросил «всегда правильное» решение? Этот ответ не очень продуктивен для обсуждения.
Shoosh
1
Ну, было много постов, обсуждающих проблемы, с которыми столкнется такая программа. Мой пост дает окончательный и правильный ответ на эту часть обсуждения. И мне бы, например, не понравилось бы, если бы программа сказала мне, я мог бы безопасно удалить #include, и тогда мой код больше не компилируется. (или хуже - все еще компилируется, но делает что-то по-другому). Любая такая программа несет этот риск.
Algoman
4
Между всеми СПЕКУЛЯЦИЯМИ о том, как тяжело это будет, и как вы МОЖЕТЕ решить то или иное препятствие, я дал вам только 100% правильный ответ. Я нахожу довольно наглым сказать, что это не было продуктивным ...
Алгоман
1
Я вспомнил, что теорема Райса гласит: «Не может быть программы, которая всегда может проверить, решает ли данная программа эту проблему излишних включений». Может быть несколько программ, которые решают проблему лишних включений.
Чжэ Ян
1
лично я нашел вклад @ Алгомана очень полезным. заставляет меня понять, насколько сложна эта проблема.
Богардон
1

Вот простой грубый способ идентификации лишних заголовков включает в себя . Это не идеально, но устраняет «очевидные» ненужные включения. Избавление от них имеет большое значение для очистки кода.

В сценарии могут быть доступны непосредственно на GitHub.

ар-экранном
источник
0

PC Lint Gimpel Software может сообщать о том, что включаемый файл был включен более одного раза в модуль компиляции , но он не может найти включаемые файлы, которые не нужны так, как вы ищете.

Редактировать: это может. См ответ егоmatt

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

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

Я обнаружил, что вы платите за эту функцию, хотя; CLion требуется время для сканирования и анализа вашего проекта при первой загрузке.

congusbongus
источник