Есть ли способ использовать предварительно скомпилированные заголовки в VC ++, не требуя stdafx.h? - PullRequest
10 голосов
/ 14 ноября 2008

У меня есть куча устаревшего кода, для которого мне нужно написать модульные тесты. Он везде использует предварительно скомпилированные заголовки, поэтому почти все файлы .cpp имеют зависимость от stdafx.h, что затрудняет нарушение зависимостей для написания тестов.

Мой первый инстинкт - удалить все эти файлы stdafx.h, которые по большей части содержат директивы #include, и при необходимости помещать эти #include непосредственно в исходные файлы.

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

Есть ли способ сохранить предварительно скомпилированные заголовки без зависимостей stdafx.h? Есть ли лучший способ решить эту проблему?

Ответы [ 9 ]

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

Да, есть лучший способ.

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

Лучший способ, опять же ИМХО, это использовать #pragma hdrstop и / Yc и / Yu. Это позволяет вам легко настраивать конфигурации сборки, в которых используются предварительно скомпилированные заголовки, а также создавать конфигурации, в которых не используются предварительно скомпилированные заголовки. Файлы, использующие предварительно скомпилированные заголовки, не имеют прямой зависимости от самого предварительно скомпилированного заголовка в исходном файле, что позволяет создавать их с предварительно скомпилированным заголовком или без него. Файл проекта определяет, какой исходный файл создает предварительно скомпилированный заголовок, а строка #pragma hdrstop в каждом исходном файле определяет, какие из них берутся из предварительно скомпилированного заголовка (если используется), а какие - непосредственно из исходного файла ... Это означает, что когда При выполнении обслуживания вы будете использовать конфигурацию, в которой не используются предварительно скомпилированные заголовки, и будет восстановлен только тот код, который необходимо перестроить после изменения файла заголовка. При выполнении полных сборок вы можете использовать предварительно скомпилированные конфигурации заголовков, чтобы ускорить процесс компиляции. Еще одна хорошая вещь, связанная с наличием опции не скомпилированного заголовка, заключается в том, что он гарантирует, что ваши cpp-файлы содержат только то, что им нужно, и включают все, что им нужно (что сложно, если вы используете «стиль мастера» скомпилированного заголовка.

Я написал немного о том, как это работает здесь: http://www.lenholgate.com/blog/2004/07/fi-stlport-precompiled-headers-warning-level-4-and-pragma-hdrstop.html (не обращайте внимания на материал о / FI), и у меня есть несколько примеров проектов, которые собираются здесь с помощью методов #pragma hdrstop и / Yc / Yu: http://www.lenholgate.com/blog/2008/04/practical-testing-16---fixing-a-timeout-bug.html.

Конечно, переход от использования скомпилированного заголовка 'wizard style' к более контролируемому стилю часто бывает нетривиальным ...

4 голосов
/ 14 ноября 2008

Когда вы обычно используете предварительно скомпилированные заголовки, stdafx.h служит двум целям. Он определяет набор стабильных, общих включаемых файлов. Также в каждом файле .cpp он служит маркером того, где заканчиваются предварительно скомпилированные заголовки.

Похоже, что вы хотите сделать, это:

  • Оставить предварительно скомпилированный заголовок включенным.
  • Оставьте файл "stdafx.h" включенным в каждый файл .cpp.
  • Очистить включения из "stdafx.h".
  • Для каждого файла .cpp определите, какие из них были необходимы из старого "stdafx.h". Добавьте их перед #include "stdafx.h" в каждом файле .cpp.

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

2 голосов
/ 14 ноября 2008

Да. Имя "stdafx.h / stdafx.pch" - это просто соглашение. Вы можете дать каждому .cpp свой собственный предварительно скомпилированный заголовок. Этого, вероятно, легче всего достичь с помощью небольшого скрипта для редактирования XML в вашем .vcproj. Недостаток: в результате получается большой стек предварительно скомпилированных заголовков, и они не разделяются между TU.

Возможно, но умно? Я не могу сказать наверняка.

2 голосов
/ 14 ноября 2008

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

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

Таким образом, это означает, что предварительно скомпилированные заголовки borland действительно сэкономили время, только если вы очень жестко включили источники в том же порядке, или если один файл включения был включен (первым) во все другие файлы ... - звучит знакомо ?! ?!

2 голосов
/ 14 ноября 2008

Нет, возможно, НЕ лучший способ.

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

(На самом деле, я не понимаю, как скомпилированная схема заголовка мешает написанию ваших юнит-тестов).

1 голос
/ 14 ноября 2008

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

Еще одна вещь, которую следует учитывать, это то, что вы можете иметь один pch на библиотеку. Таким образом, вы можете разбить свой код на более мелкие библиотеки, и у каждой из них будет более жесткий набор зависимостей.

1 голос
/ 14 ноября 2008

Мой совет - не удаляйте предварительно скомпилированные заголовки, если вы не хотите, чтобы ваши сборки мучительно замедлялись. У вас есть три варианта:

  1. Избавиться от предварительно скомпилированных заголовков (не рекомендуется)
  2. Создать отдельную библиотеку для устаревшего кода; таким образом, вы можете построить его отдельно.
  3. Использование нескольких предварительно скомпилированных заголовков в одном проекте. Вы можете выбрать отдельные файлы C ++ в вашем обозревателе решений и указать им, какой заголовок должен быть предварительно скомпилирован. Вам также потребуется настроить ваш OtherStdAfx.h / cpp для генерации предварительно скомпилированного заголовка.
0 голосов
/ 20 февраля 2016

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

pch1.h:

#include <bigHeader1.h>
#include ...


pch1.cpp:

#include "pch1.h"


source1.cpp:

#include "pch1.h"
[code]


pch2.h:

#include <bigHeader2.h>
#include ...


pch2.cpp:

#include "pch2.h"


source2.cpp

#include "pch2.h"
[code]

Выберите pch1.cpp , щелкните правой кнопкой мыши, Свойства, Свойства конфигурации, C / C ++, Предварительно скомпилированные заголовки .
Предварительно скомпилированный заголовок: Создать (/ Yc)
Файл скомпилированного заголовка: pch1.h
Выходной файл скомпилированного заголовка : $ (intDir) pch1.pch

Выберите source1.cpp
Предварительно скомпилированный заголовок: Использование (/ Yu)
Предварительно скомпилированный заголовочный файл: pch1.h
Выходной файл скомпилированного заголовка : $ (intDir) pch1.pch (я не думаю, что это имеет значение для / Yu)

Сделайте то же самое для pch2.cpp и source2.cpp , за исключением того, что для заголовочного файла и выходного файла заголовка установлено значение pch2.h и pch2.pch . Это работает для меня.

0 голосов
/ 14 ноября 2008

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

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

G.

...