Как я могу обнаружить ненужные файлы #include в большом проекте на C ++?

Я работаю над большим проектом C ++ в Visual Studio 2008, и есть много файлов с ненужными директивами #include . Иногда #include s – только артефакты, и все будет скомпилировано с удалением, а в других случаях classы могут быть объявлены вперед, а #include можно перенести в файл .cpp . Есть ли хорошие инструменты для обнаружения обоих этих случаев?

Хотя он не будет отображать ненужные файлы include, Visual studio имеет параметр /showIncludes (щелкните правой кнопкой мыши на .cpp файле, Properties->C/C++->Advanced ), который выведет дерево всех включенных файлов во время компиляции. Это может помочь в определении файлов, которые не нужно включать.

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

PC Lint работает достаточно хорошо для этого, и он находит для вас всевозможные другие проблемы. Он имеет параметры командной строки, которые можно использовать для создания внешних инструментов в Visual Studio, но я обнаружил, что с помощью Visual Lint addin легче работать. Даже бесплатная версия Visual Lint помогает. Но дайте PC-Lint выстрел. Конфигурируя его, чтобы он не выдавал слишком много предупреждений, требуется немного времени, но вы будете поражены тем, что он появляется.

Есть новый инструмент, основанный на Clang, который включает в себя – что вы используете , и это делается для этого.

!! ОТКАЗ !! Я работаю над инструментом коммерческого статического анализа (не PC Lint). !! ОТКАЗ !!

Существует несколько проблем с простым подходом к анализу:

1) Комплекты перегрузки:

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

2) Специализация шаблонов:

Подобно примеру перегрузки, если у вас есть частичная или явная специализация для шаблона, вы хотите, чтобы все они были видимыми при использовании шаблона. Может быть, специализации для основного шаблона находятся в разных файлах заголовков. Удаление заголовка со специализацией не приведет к ошибке компиляции, но может привести к неопределенному поведению, если эта специализация была бы выбрана. (См .: Видимость специализированной специализации функции C ++ )

Как указано в «msalters», выполнение полного анализа кода также позволяет анализировать использование classа. Проверяя, как class используется по конкретному пути к файлам, возможно, что определение classа (и, следовательно, всех его зависимостей) может быть полностью или хотя бы перенесено на уровень, близкий к основному источнику в include дерево.

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

Скажем, ваш исходный файл включает ah и bh; ah содержит #define USE_FEATURE_X и bh использует #ifdef USE_FEATURE_X . Если #include "ah" закомментирован, ваш файл все еще может компилироваться, но может не делать того, что вы ожидаете. Обнаружение этого программно нетривиально.

Независимо от того, какой инструмент вам понадобится, вы также должны знать свою среду сборки. Если ах выглядит так:

 #if defined( WINNT ) #define USE_FEATURE_X #endif 

Тогда USE_FEATURE_X определяется только в том случае, если WINNT определен, поэтому инструмент должен знать, какие директивы генерируются самим компилятором, а также какие из них указаны в команде компиляции, а не в файле заголовка.

Как и Timmermans, я не знаком с инструментами для этого. Но я знал программистов, которые писали скрипт Perl (или Python), чтобы попробовать комментировать каждый из них, включая строку по одному, а затем скомпилировать каждый файл.


Похоже, сейчас у Эрика Раймонда есть инструмент для этого .

Google cpplint.py имеет правило «включить то, что вы используете» (среди многих других), но, насколько я могу судить, «включить только то, что вы используете». Тем не менее, это может быть полезно.

Если вас интересует эта тема в целом, вы можете проверить Lacos ‘ Large Scale C ++ Software Design . Он немного устарел, но затрагивает множество вопросов «физического дизайна», таких как поиск абсолютного минимума заголовков, которые необходимо включить. Я на самом деле не видел такого, что обсуждалось где-то еще.

Если ваши файлы заголовков обычно начинаются с

 #ifndef __SOMEHEADER_H__ #define __SOMEHEADER_H__ // header contents #endif 

(в отличие от использования однажды #pragma) вы можете изменить это на:

 #ifndef __SOMEHEADER_H__ #define __SOMEHEADER_H__ // header contents #else #pragma message("Someheader.h superfluously included") #endif 

А поскольку компилятор выводит имя скомпилированного файла cpp, это даст вам знать, по крайней мере, какой файл cpp вызывает многократный ввод заголовка.

Попробуйте включить Include Manager . Он легко интегрируется в Visual Studio и визуализирует ваши пути включения, которые помогают вам находить ненужные вещи. Внутри он использует Graphviz, но есть еще много интересных функций. И хотя это коммерческий продукт, он имеет очень низкую цену.

Вы можете построить граф включений, используя C / C ++ Include File Dependencies Watcher , а find unneeded – визуально.

PC-Lint действительно может это сделать. Один простой способ сделать это – настроить его для обнаружения только неиспользуемых файлов include и игнорировать все другие проблемы. Это довольно просто – включить только сообщение 766 («Файл заголовка, не используемый в модуле»), просто включите в командной строке опции -w0 + e766.

Такой же подход можно также использовать с соответствующими сообщениями, такими как 964 («Файл заголовка, непосредственно не используемый в модуле») и 966 («Косвенный заголовочный файл, который не используется в модуле»).

FWIW Я писал об этом более подробно в блоге на прошлой неделе по адресу http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318 .

Если вы хотите удалить ненужные файлы #include , чтобы сократить время сборки, ваше время и деньги можно потратить на распараллеливание процесса сборки с помощью cl.exe / MP , make -j , Xoreax IncrediBuild , distcc / icecream и т. Д.

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

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

Для каждого файла include и source, закомментируйте каждый из них каждый файл по одному и посмотрите, компилируется ли он.

Также неплохо отсортировать файлы include в алфавитном порядке, а там, где это невозможно, добавьте комментарий.

Добавление одного или обоих из следующих #defines исключает часто ненужные файлы заголовков и может существенно улучшить время компиляции, особенно если код, который не использует функции Windows API.

 #define WIN32_LEAN_AND_MEAN #define VC_EXTRALEAN 

См. http://support.microsoft.com/kb/166474

Если вы еще этого не сделали, используя предварительно скомпилированный заголовок, чтобы включить все, что вы не собираетесь изменять (заголовки плат, внешние заголовки SDK или статические, уже завершенные части вашего проекта), будут иметь огромное значение в времени сборки.

http://msdn.microsoft.com/en-us/library/szfdksca(VS.71).aspx

Кроме того, хотя может быть слишком поздно для вашего проекта, организация вашего проекта на разделы и не слияние всех локальных заголовков с одним большим основным заголовком – это хорошая практика, хотя для этого требуется небольшая дополнительная работа.

Если вы будете работать с Eclipse CDT, вы можете попробовать http://includator.com, чтобы оптимизировать свою структуру include. Тем не менее, Includator может не знать достаточно о предопределенных включениях VC ++ и настройке CDT для использования VC ++ с правильными включениями еще не встроен в CDT.

Последняя версия Jetbrains IDE, CLion, автоматически отображает (в сером цвете) включения, которые не используются в текущем файле.

Также возможно иметь список всех неиспользуемых включает (а также функции, методы и т. Д.) Из среды IDE.

Некоторые из существующих ответов говорят, что это сложно. Это действительно так, потому что вам нужен полный компилятор, чтобы обнаружить случаи, в которых было бы подходящим объявление. Вы не можете анализировать C ++, не зная, что означают символы; грамматика просто слишком неоднозначна для этого. Вы должны знать, называется ли какое-либо имя classу (может быть объявлено по прогрессу) или переменной (не может). Кроме того, вам необходимо знать пространство имен.

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

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

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

Если есть определенный заголовок, который, по вашему мнению, больше не нужен (скажем, string.h), вы можете прокомментировать, что включить, а затем поставить это ниже всех включений:

 #ifdef _STRING_H_ # error string.h is included indirectly #endif 

Конечно, ваши заголовки интерфейса могут использовать другое соглашение #define для записи их включения в CPP-память. Или нет конвенции, и в этом случае такой подход не будет работать.

Затем перестройте. Существует три возможности:

  • Он строит нормально. string.h не был критичным для компиляции, а include для него можно удалить.

  • #Error поездки. string.g был включен косвенно каким-то образом. Вы все еще не знаете, требуется ли string.h. Если это необходимо, вы должны прямо # включить его (см. Ниже).

  • Вы получаете другую ошибку компиляции. string.h был необходим и не включается косвенно, поэтому включение было правильным для начала.

Обратите внимание, что в зависимости от косвенного включения, когда ваши .h или .c напрямую используют другой .h, почти наверняка ошибка: вы фактически обещаете, что ваш код будет требовать только этот заголовок, если для этого нужен какой-то другой заголовок, который вы используете, что, вероятно, не то, что вы имели в виду.

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

  • Кодирование передачи контента 7 бит или 8 бит
  • проблема с оберткой заголовков столбцов jqGrid в IE
  • Как связать несколько файлов реализации в C
  • Когда статические определения функций в файлах заголовков в C?
  • Защитники заголовков в C ++
  • Android: наложение на просмотр камеры Android
  • Как выглядит заголовок zlib?
  • Interesting Posts

    Google начал выделять области поиска в розовом цвете. Эта функция доступна в Google Maps API 3?

    Предоставить доступ к моей локальной компьютерной форме вне

    Как использовать функцию rand для создания чисел в определенном диапазоне?

    Что такое «N» версии Windows 8?

    Пользовательская строка числового формата для отображения знака

    Вызывающий stream должен быть STA, потому что многие компоненты пользовательского интерфейса требуют этого

    Не работает привязка клавиш Tmux

    Чтение из текстового файла до тех пор, пока EOF не повторит последнюю строку

    В чем смысл «финального classа» в Java?

    Координата оси координат пикселя? (Matlab)

    Проблема с диалоговыми windowsми jQuery и плагинами Datepicker

    Добавление веб-службы в уже ansible Java-проект

    Android – Съемка фотографий и сохранение их с помощью специального имени в пользовательское место назначения через Intent

    Как бы вы сравнили два XML-документа?

    Индексирующие векторы и массивы с +:

    Давайте будем гением компьютера.