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



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

858   20  

20 ответов:

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

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

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

есть новый инструмент на основе Clang,include-what-you-use, которое стремится сделать это.

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

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

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

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

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

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

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

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

скажем, ваш исходный файл включает в себя 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, а не в заголовочном файле.

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


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

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

Если вы заинтересованы в этой теме в целом, вы можете проверить Lakos' Разработка Крупномасштабного Программного Обеспечения C++. Это немного устарело, но входит в множество проблем "физического дизайна", таких как поиск абсолютного минимума заголовков, которые должны быть включены. Я действительно не видел, чтобы такие вещи обсуждались где-либо еще.

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

#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 вызывает заголовок, который будет введен несколько раз.

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

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

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/мороженое и т. д.

конечно, если у вас уже есть параллельный процесс сборки, и вы все еще пытаетесь его ускорить, то обязательно очистите свой #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 для оптимизации структуры включения. Однако Includator может недостаточно знать о предопределенных включениях VC++, а настройка CDT для использования VC++ с правильными включениями еще не встроена в CDT.

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

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

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

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

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

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

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

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

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

затем восстановить. Есть три возможности:

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

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

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

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

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

Comments

    Ничего не найдено.