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

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

Ответы [ 20 ]

45 голосов
/ 16 сентября 2008

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

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

28 голосов
/ 16 сентября 2008

PC Lint отлично подходит для этого, и он находит для вас и всякие другие глупые проблемы. У него есть параметры командной строки, которые можно использовать для создания внешних инструментов в Visual Studio, но я обнаружил, что надстройка Visual Lint легче работать. Помогает даже бесплатная версия Visual Lint. Но попробуйте PC-Lint. Настройка его так, чтобы он не давал вам слишком много предупреждений, занимает немного времени, но вы будете удивлены тем, что он получится.

26 голосов
/ 24 февраля 2011

Существует новый инструмент на основе Clang, include-what-you-use , предназначенный для этого.

25 голосов
/ 17 сентября 2008

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

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

1) Наборы перегрузки:

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

2) Шаблон специализации:

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

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

10 голосов
/ 16 сентября 2008

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

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

Какой бы инструмент это ни делал, он должен знать и вашу среду сборки. Если a.h выглядит так:

#if defined( WINNT )
   #define USE_FEATURE_X
#endif

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

9 голосов
/ 16 сентября 2008

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


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

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

5 голосов
/ 08 ноября 2008

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

4 голосов
/ 26 июня 2009

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

4 голосов
/ 30 марта 2010

Вы можете построить график включения, используя C / C ++ Watcher Зависимости файлов включений , и визуально находить ненужные включения.

3 голосов
/ 21 сентября 2008

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

#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 вызывает многократный перенос заголовка.

...