2008-09-21 4 views
4

Любые рекомендуемые методы очистки «заголовочных спагетти», которые приводят к чрезвычайно медленному времени компиляции (Linux/Unix) ?Очистка устаревшего кода «header spaghetti»

Есть ли равные значения для «# прагма один раз» с GCC?
(найдено противоречивых сообщений относительно этого)

Спасибо.

ответ

8

Предполагая, что вы знакомы с «включите охрану» (#ifdef в начале заголовка ..), дополнительным способом ускорения времени сборки является использование внешних включенных охранников , Обсуждалось в «Large Scale C++ Software Design». Идея состоит в том, что классика включает в себя защитные устройства, в отличие от #pragma один раз, не избавляет вас от синтаксического анализа препроцессора, требуемого для игнорирования заголовка со второго раза (т. Е. Он все еще должен разбираться и искать начало и конец защитника включения. external include guard, вы помещаете # ifdef вокруг строки #include.

Так это выглядит следующим образом:

#ifndef MY_HEADER 
#include "myheader.h" 
#endif 

и, конечно, в файле H у Вас есть классический включают охранник

#ifndef MY_HEADER 
#define MY_HEADER 

// content of header 

#endif 

Этот путь файл myheader.h даже не открыт/анализируется препроцессором, и это может сэкономить вам много времени в крупных проектах, особенно когда файлы заголовков размещаются в общих удаленных местах, как это иногда бывает.

еще раз, это все в этой книге. hth

+0

Единственная проблема с ними в том, что они выглядят ужасно. – 2008-09-21 10:02:02

+0

И они добавляют скрытую зависимость, то есть с внешними защитами, файлы знают определения, защищающие их включенные файлы. Но если у вас есть способ убедиться, что ваши охранники уникально сгенерированы (например, с использованием полного имени проекта и файла), то это не должно быть проблемой. – paercebal 2008-09-21 10:16:49

0

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

0

Как уже упоминалось в другом ответе, вы обязательно должны использовать форвардные декларации, когда это возможно. Насколько мне известно, GCC не имеет ничего эквивалентного #pragma один раз, поэтому я придерживаюсь стиля старой моды, включающего охранников.

0

Спасибо за ответы, но вопрос касается существующего кода, который включает строгий «включить заказ» и т. Д. Вопрос в том, есть ли какие-либо инструменты/скрипты, чтобы прояснить, что происходит на самом деле.

Header охранники Арент решение, как они не препятствуют компилятору снова и снова читает весь файл ...

+0

1 - Вы должны отредактировать/обновить исходный вопрос ... 2 - Если у вас есть строгие заказы, тогда у вас есть скрытые зависимости, не материализованные вашими включениями. Это не будет захвачено инструментами/скриптами. 3 - Как написано rubancache, Doxygen + Grapviz может сделать график или ваши включения. – paercebal 2008-09-21 10:38:32

4

Я читал, что GCC считает #pragma once устаревшим, хотя даже #pragma once только может сделать так много ускоряйте все.

Чтобы попытаться распутать спагетти #include, вы можете посмотреть Doxygen. Он должен иметь возможность генерировать графики включенных заголовков, что может дать вам преимущество в упрощении. Я не могу вспомнить подробности, но функции графика могут потребовать установки GraphViz и указать Doxygen путь, где он может найти dotty.exe GraphViz.

Другой подход, который вы могли бы рассмотреть, если ваше время компиляции является вашей основной задачей, заключается в настройке Precompiled Headers.

2

Используйте один или несколько из тех, для ускорения времени сборки

  1. Использование предварительно скомпилированных заголовков
  2. использовать механизм кэширования (SCons, например)
  3. Использование распределенной системы сборки (Distcc, IncrediBuild ($))
6

Если вы хотите сделать полную очистку и у вас есть время, чтобы сделать это, лучшим решением будет удалить все #includes во всех файлах (за исключением очевидных, например abc.h в abc.cpp) и затем скомпилируйте проект. Добавьте необходимую декларацию или заголовок для исправления первой ошибки, а затем повторите, пока вы не закончите чисто.

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

0

PC-Lint долгий путь к очистке заголовков спагетти. Также он будет решать другие проблемы для вас тоже, как неинициализированные переменные, которые не видны и т. Д.

3

Ричард был несколько прав (почему его решение было записано?).

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

При этом, как:

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

2 - Ваш старый код по-прежнему живет. Затем вы либо используете предварительно скомпилированные заголовки, либо/или охранники/внешние охранники для временного решения, но в конце вам нужно будет удалить все ваши включенные, один .C или .CPP за раз и скомпилировать каждый. C или .CPP по одному, исправляя их включение с помощью forward-declarations или включая при необходимости (или даже разбивая большой, включайте в более мелкие, чтобы каждый файл .C или .CPP получал только те заголовки, которые ему нужны). В любом случае, тестирование и удаление устаревших включений является частью технического обслуживания проекта, поэтому ...

Мой собственный опыт с предварительно скомпилированными заголовками был не совсем хорошим, потому что в течение половины времени компилятор не мог найти символ, который у меня был и поэтому я попробовал полную «чистую/перестроить», чтобы убедиться, что не был предварительно скомпилированный заголовок, который был устаревшим. Поэтому я предполагаю использовать его для внешних библиотек, которые вы даже не будете касаться (например, заголовки STL, C API, Boost, что угодно). Тем не менее, мой собственный опыт был с Visual C++ 6, так что я думаю (надеюсь?), Они поняли это правильно.

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

#include "AAA.hpp" 
#include "BBB.hpp" 

Но нет:

#include "BBB.hpp" 
#include "AAA.hpp" 

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

0

Как onebyone.livejournal.com прокомментировал в ответ на ваш вопрос, некоторые компиляторы поддерживают include guard optimization, что страница я связывал определяется следующим образом:

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

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

  • Каждый .c или .cpp файл должен #include соответствующий .h файл первым, а остальные ее #include директивы должны быть отсортированы в алфавитном порядке. Обычно вы получаете ошибки сборки, когда это нарушает неопределенные зависимости между заголовочными файлами.
  • Если у вас есть файл заголовок, который определяет глобальные определений типов для основных типов или глобальных #define директив, которые используются для большинства коды, каждый .h файла должен #include этого файл первого, а остальное ее #include директивы должны быть отсортированы в алфавитном порядке.
  • Когда эти изменения вызывают ошибки компиляции, вам обычно нужно добавить явную зависимость от одного файла заголовка к другому, в форме #include.
  • Если эти изменения не вызывают ошибок компиляции, они могут привести к поведенческим изменениям. Надеюсь, у вас есть своего рода набор тестов, который вы можете использовать для проверки функциональности вашего приложения.

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

3

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

  • найти все #include заявления
  • удалить одно заявление в то время, и компиляции
  • если сборника сбой, добавьте оператор include обратно в

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

еще несколько заметок:

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

  • GCC поддерживает #pragma один раз, но называет его «устаревшим»
  • Прагма раз ISN» t поддерживается всеми компиляторами, а не частью стандарта C

  • Не только компиляторы могут быть проблематичными. Такие инструменты, как Incredibuild, также имеют проблемы с #pragma один раз
Смежные вопросы