Есть ли способ использовать предварительно скомпилированные заголовки в VС++ без требования stdafx.h?

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

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

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

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

Ответ 1

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

Проблема, 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.

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

Ответ 2

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

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

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

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

Ответ 3

Нет, вероятно, нет лучшего способа.

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

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

Ответ 4

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

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

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

Ответ 5

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

Возможно, но умный? Я не могу сказать точно.

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

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

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

G.

Ответ 9

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

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/С++, Предварительно скомпилированные заголовки.
Предварительно скомпилированный заголовок: Создать (/Yc)
Предварительно скомпилированный файл заголовка: pch1.h
Файл с предварительно скомпилированным выходным файлом: $(intDir) pch1.pch

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

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