Stdafx h как подключить visual studio 2019

Обновлено: 06.07.2024

С опцией «Показать все файлы» в VS, я добавил папку и создал новый класс в этой папке. Так как я использую предварительно скомпилированные заголовки, мне также нужно включить stdafx.h, который находится в корневом каталоге относительно нового файла класса.

В моем файле CPP у меня есть

Все же я получаю следующую ошибку:

Насколько я понимаю, что .. должен дать указание компилятору перейти на один уровень выше?

Решение

Надеюсь, вы поймаете перерыв.

Другие решения

Интересно, что хитрость, которую я использую, не в ответах:

Не меняйте файл CPP; сохраняйте заголовочные файлы такими, какие они есть. Сборка как есть.

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

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

Поместите каталог, где stdafx.h лежит в каталогах включения компилятора.

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

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

Вы можете настроить параметры предварительно скомпилированного заголовка для каждого файла.

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

которые не дают много указаний относительно этого или любого другого подхода.

Исходя из этого, стоит идти с альтернативами, такими как $(ProjectDir) на пути C ++ Include (в C ++ \ General), но это может вызвать путаницу при включении других заголовочных файлов.

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

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

Конфигурация заголовка прекомпилятора установлена ​​на глобальном уровне, но может быть переопределена для каждого файла. Обычная глобальная конфигурация (доступная через Свойства проекта -> Свойства конфигурации -> C / C ++ -> Предварительно скомпилированные заголовки):

Эта конфигурация применяется ко всем файлам в проекте по умолчанию. Однако конфигурация для stdafx.cpp устанавливается на уровне файлов и переопределяет значение предварительно скомпилированного заголовка на:

Результатом этого является то, что для любого исходного файла, настроенного для использования предварительно скомпилированного заголовка (который по умолчанию является всеми из них, кроме stdafx.cpp), VS будет искать директиву include, которая соответствует настроенному значению предварительно скомпилированного файла заголовка. например

Поскольку проверка использует простое сравнение строк вместо любого вида поиска в каталоге, то (независимо от расположения исходного файла относительно корневого каталога проекта или расположения файла stdafx.h) путь и имя файла используются в include директива должна соответствовать именно так используется настройкой конфигурации файла скомпилированного заголовочного файла проекта. Неожиданным побочным эффектом этого является то, что если у вас есть подкаталог проекта, содержащий различные исходные файлы, в этих файлах вам не нужно ссылаться на файл stdafx.h, используя относительный путь, например .. \ stdafx.h (и если вы используете VS выдаст ошибку, сообщающую, что он столкнулся с концом файла при поиске предварительно скомпилированного заголовка).

Я бы предложил использовать:

как «Предварительно скомпилированный заголовочный файл»
а также «Дополнительно> Принудительно включить файл»

Это будет автоматически включать pch для тебя .cpp файлов в начале, поэтому ничего не нужно менять в .cpp файлы.

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

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

При создании нового проекта в Visual Studio в проект добавляется файл предкомпилированного заголовка с именем pch.h. (В Visual Studio 2017 и более ранних версиях файл назывался stdafx.h.) Цель файла — ускорить процесс сборки. Здесь следует включить любые стабильные файлы заголовков, например заголовки стандартной библиотеки, такие как <vector> . Предкомпилированный заголовок компилируется только в том случае, если он или какие-либо содержащиеся в нем файлы были изменены. Если изменения вносятся только в исходный код проекта, при сборке будет пропущена компиляция для предкомпилированного заголовка.

Параметры компилятора для предкомпилированных заголовков — /Y. На страницах свойств проекта параметры находятся в разделе Свойства конфигурации > C/C++ > Предварительно скомпилированные заголовки. Можно не использовать предкомпилированные заголовки, а также указать имя файла заголовка и путь к выходному файлу.

Настраиваемый предварительно скомпилированный код

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

Случаи использования предварительной компиляции исходного кода

Предварительно скомпилированный код полезен во время цикла разработки, чтобы сократить время компиляции, особенно если:

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

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

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

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

Хотя для каждого исходного файла можно использовать только один файл предкомпилированного заголовка (PCH), в проекте можно использовать несколько файлов PCH.

Два варианта предварительной компиляции кода

Можно предварительно скомпилировать любой код C или C++ — вы не ограничены предварительной компиляцией только файлов заголовков.

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

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

Параметры предкомпилированного заголовка см. в разделах /Yc (создание файла предкомпилированного заголовка) и /Yu (использование файла предкомпилированного заголовка). Для создания предкомпилированного заголовка используйте /Yc. При использовании с необязательной прагмой hdrstop параметр /Yc позволяет предварительно компилировать как файлы заголовков, так и исходный код. Выберите /Yu, чтобы использовать существующий предкомпилированный заголовок в существующей компиляции. Можно также использовать /Fp с параметрами /Yc и /Yu, чтобы предоставить альтернативное имя предкомпилированному заголовку.

В справочных разделах, посвященных параметрам компилятора /Yu и /Yc, обсуждаются способы доступа к этой функции в среде разработки.

Правила согласованности предкомпилированных заголовков

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

Правила целостности для пофайлового использования предкомпилированных заголовков

Параметр компилятора /Yu позволяет указать, какой файл PCH следует использовать.

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

Согласованность параметров компилятора

Следующие параметры компилятора могут вызвать предупреждение о несогласованности при использовании файла PCH:

Макросы, созданные с помощью параметра препроцессора (/D), должны быть одинаковыми в компиляциях, создавшими файл PCH, и текущей компиляцией. Состояние определенных констант не проверяется, но при их изменениях могут возникать непредсказуемые результаты.

Файлы PCH не работают с параметрами/E и /EP.

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

C7 совместимо (/Z7)

Если при создании файла PCH этот параметр включен, последующие компиляции, использующие файл PCH, могут использовать отладочную информацию.

Если при создании файла PCH параметр "C7 совместимо (/Z7)" отключен, последующие компиляции, использующие файл PCH и/Z7, вызывают предупреждение. Отладочная информация помещается в текущий OBJ-файл, а локальные символы, определенные в файле PCH, недоступны отладчику.

Согласованность пути включаемых файлов

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

Согласованность исходного файла

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

Согласованность прагмы

Прагмы, обработанные во время создания файла PCH, обычно влияют на файл, с которым впоследствии используется файл PCH. Прагмы comment и message не влияют на оставшуюся часть компиляции.

Они влияют только на код в файле PCH; они не влияют на код, который впоследствии использует файл PCH:

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

alloc_text
auto_inline
check_stack
code_seg
data_seg

function
include_alias
init_seg
inline_depth

inline_recursion
intrinsic
optimize
pack

pointers_to_members
setlocale
vtordisp
warning

Правила целостности для параметров компилятора /Yc и /Yu

При использовании предкомпилированного заголовка, созданного с помощью параметра /Yc или/Yu, компилятор сравнивает текущую среду компиляции с той, которая существовала при создании файла PCH. Не забудьте указать среду, соответствующую предыдущей (с помощью параметров компилятора, прагм и т. д.) для текущей компиляции. Если компилятор обнаруживает несогласованность, он выдает предупреждение и по возможности определяет несогласованность. Такие предупреждения не обязательно указывают на проблему с файлом PCH. Они просто предупреждают о возможных конфликтах. В следующих разделах объясняются требования к согласованности для предкомпилированных заголовков.

Согласованность параметров компилятора

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

Параметр name Правило
/D Определение констант и макросов Должны быть одинаковыми в компиляции, создавшей предкомпилированный заголовок, и в текущей компиляции. Состояние определенных констант не проверяется, но если файлы зависят от значений измененных констант, могут возникать непредсказуемые результаты.
/E или /EP Копирование выходных данных препроцессора в стандартный вывод Предкомпилированные заголовки не работают с параметром /E или /EP.
/FR или /FR Создание сведений о браузере исходного кода Майкрософт Чтобы параметры /Fr и /FR были допустимыми вместе с параметром /Yu, они также должны применяться при создании предкомпилированного заголовка. При последующих компиляциях, использующих предкомпилированный заголовок, также создаются сведения о браузере исходного кода. Сведения о браузере помещаются в один SBR-файл, и на него ссылаются другие файлы так же, как и сведения CodeView. Нельзя переопределить размещение сведений о браузере исходного кода.
/GA, /GD, /GE, /Gw или /GW Параметры протокола Windows Должны быть одинаковыми в компиляции, создавшей предкомпилированный заголовок, и в текущей компиляции. Если эти параметры различаются, выдается предупреждение.
/ZI Создание полной отладочной информации Если при создании предкомпилированного заголовка этот параметр включен, последующие компиляции, использующие предварительную компиляцию, могут использовать отладочную информацию. Если при создании предкомпилированного заголовка параметр /Zi не был включен, последующие компиляции, использующие предварительную компиляцию и параметр /Zi, выдают предупреждение. Отладочная информация помещается в текущий OBJ-файл, а локальные символы, определенные в предкомпилированном заголовке, недоступны отладчику.

Средство предварительно откомпилированных заголовков предназначено для использования только в исходных файлах C и C++.

Использование предкомпилированных заголовков в проекте

В предыдущих разделах представлен обзор предкомпилированных заголовков: /Yc и /Yu, параметра /FP и прагмы hdrstop. В этом разделе описывается метод использования в проекте параметров заголовков, предкомпилированных вручную. В конце раздела приводится пример файла makefile и кода, который управляет им.

Чтобы ознакомиться с другим подходом к использованию в проекте параметров заголовков, предкомпилированных вручную, изучите один из файлов makefile, расположенных в каталоге MFC\SRC, который создается во время установки Visual Studio по умолчанию. Подход в этих файлах makefile аналогичен представленному в этом разделе, однако в них используются макросы служебной программы обслуживания Майкрософт (NMAKE) и обеспечивается больший контроль над процессом сборки.

PCH-файлы в процессе построения

База кода проекта программного обеспечения обычно содержится в нескольких исходных файлах C или C++, объектных файлах, библиотеках и файлах заголовков. Как правило, файл makefile координирует объединение этих элементов в исполняемый файл. На следующем рисунке показана структура файла makefile, использующего файл предкомпилированного заголовка. Имена макросов и имена файлов на рисунке соответствуют приведенным в примере кода в разделах Пример файла makefile для PCH и Пример кода для PCH.

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


Структура файла makefile, использующего файл предкомпилированного заголовка

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

CL /c /W3 /Yc$(BOUNDRY) applib.cpp myapp.cpp

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

Файлы, перечисленные в этих макросах, могут быть файлами заголовков или файлами исходного кода C или C++. (Один файл PCH нельзя использовать одновременно с модулями C и C++.) Обратите внимание, что можно использовать макрос hdrstop, чтобы прервать предварительную компиляцию в любой момент в файле BOUNDRY. Дополнительные сведения см. в разделе hdrstop.

Далее APPLIB.obj на схеме представляет код поддержки, используемый в окончательном приложении. Он создается из APPLIB.cpp, файлов, перечисленных в макросе UNSTABLEHDRS, и предварительно скомпилированного кода из предкомпилированного заголовка.

MYAPP.obj представляет конечное приложение. Он создается из файла MYAPP.cpp, файлов, перечисленных в макросе UNSTABLEHDRS, и предварительно скомпилированного кода из предкомпилированного заголовка.

Наконец, создается исполняемый файл (MYAPP.EXE) путем связывания файлов, перечисленных в макросе OBJS (APPLIB.obj и MYAPP.obj).

Образец файла makefile для PCH

В следующем файле makefile используются макросы и структура команд потока управления !IF, !ELSE, !ENDIF для упрощения адаптации к проекту.

Помимо макросов STABLEHDRS, BOUNDRY и UNSTABLEHDRS, показанных на рисунке "Структура файла makefile, использующего файл предкомпилированного заголовка" в разделе PCH-файлы в процессе сборки, этот файл makefile предоставляет макрос CLFLAGS и макрос LINKFLAGS. Эти макросы необходимо использовать для перечисления параметров компилятора и компоновщика, которые применяются при сборке отладочной или финальной версии исполняемого файла приложения. Также имеется макрос LIBS, в котором перечислены библиотеки, необходимые для проекта.

Файл makefile также использует структуру команд !IF, !ELSE, !ENDIF для обнаружения того, определен ли символ DEBUG в командной строке NMAKE:

Эта функция позволяет использовать один и тот же файл makefile во время разработки и для финальных версий программы — используйте DEBUG=0 для финальных версий. Следующие строки команды являются эквивалентными.

Дополнительные сведения о файлах makefile см. в разделе Справочник по NMAKE. См. также разделы Параметры компилятора MSVC и Параметры компоновщика MSVC.

Пример кода для PCH

Следующие исходные файлы используются в файле makefile, который описан в разделах PCH-файлы в процессе сборки и Образец файла makefile для PCH. Обратите внимание, что комментарии содержат важную информацию.

StdAfx.h, Precompiled headers


Статья рассчитана на людей, которые знакомятся со средой Visual Studio и пытаются компилировать в ней свои Си++-проекты. В незнакомой среде всё кажется странным и непонятным. Особенно новичков раздражает файл stdafx.h, из-за которого возникают странные ошибки во время компиляции. Очень часто всё заканчивается тем, что новичок долгое время везде старательно отключает Precompiled Headers. Чтобы помочь людям разобраться что к чему, и была написана эта статья.

Для чего нужны Precompiled Headers

Precompiled headers предназначены для ускорения сборки проектов. Обычно программисты начинают знакомиться с Visual C++, используя крошечные проекты. На них сложно заметить выигрыш от precompiled headers. Что с ними, что без них, на глаз программа компилируется одинаковое время. Это добавляет путаницы. Человек не видит для себя пользы от этого механизма и решает, что он для специфичных задач и ему никогда не понадобится. И иногда считает так многие годы.

На самом деле, precompiled headers весьма полезная технология. Пользу от него можно заметить, даже если в проекте всего несколько десятков файлов. Особенно выигрыш становится заметен, если используются такие тяжёлые библиотеки как boost.

Если посмотреть *.cpp файлы в проекте, то можно заметить, что во многие включаются одни и те-же наборы заголовочных файлы. Например, <vector>, <string>, <algorithm>. В свою очередь, эти файлы включают другие заголовочные файлы и так далее.

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

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

Как работают Precompiled Headers

Файл, который содержит precompiled headers, имеет расширение ".pch". Имя файла обычно совпадает с названием проекта. Естественно, это и другие используемые имена можно изменить в настройках. Файл может быть весьма большим и зависит от того, как много заголовочных файлов в нём раскрыто. Например, в проекте PVS-Studio он занимает около 3 мегабайт.

В файле «stdafx.h» находится самое интересное. Сюда нужно включить заголовочные файлы, которые будут заранее препроцессироваться. В качестве примера, вот файл stdafx.h, используемый нами в PVS-Studio (файл сокращён для статьи):

Теперь во все файлы *.c/*.cpp следует включить «stdafx.h». Заодно стоит удалить из этих файлов заголовки, которые уже включаются с помощью «stdafx.h».

  • Файл A: <vector>, <string>
  • Файл B: <vector>, <algorithm>
  • Файл C: <string>, <algorithm>

Достаточно сделать один precompiled header, в котором будут раскрыты <vector>, <string> и <algorithm>. Выигрыш от того, что при препроцессировании не надо читать множество файлов и вставлять их друг друга намного больше, чем потери на синтаксический анализ лишних фрагментов кода.

Как использовать Precompiled Headers

При создании нового проекта Wizard в Visual Studio создаёт два файла: stdafx.h и stdafx.cpp. Именно с помощью них и реализуется механизм precompiled headers.

На самом деле, эти файлы могут называться, как угодно. Важно не название, а параметры компиляции в настройках проекта.

В *.c/*.cpp файле можно использовать только один precompiled header. Однако, в одном проекте может присутствовать несколько разных precompiled headers. Пока будем считать, что он у нас только один.

Итак, если вы воспользовались wizard-ом, то у вас уже есть файлы stdafx.h и stdafx.cpp. Плюс выставлены все необходимые ключи компиляции.

  1. Во всех конфигурациях для всех *.c/*.cpp файлов включаем использование precompiled headers. Это делается на вкладке «Precompiled Header»:
    1. Выставляем для параметра «Precompiled Header» значение «Use (/Yu)».
    2. Для параметра «Precompiled Header File» указываем «stdafx.h».
    3. Для параметра «Precompiled Header Output File» указываем "$(IntDir)$(TargetName).pch".

    Заголовочный файл «stdafx.h» должен включаться в *.c/*.cpp файл самым первым. Обязательно! Иначе всё равно возникнут ошибки компиляции.

    Если подумать, в этом есть логика. Когда файл «stdafx.h» находится в самом начале, то можно подставить уже препроцессированный текст. Этот текст всегда одинаков и ни от чего не зависит.

    Life hack

    Есть вариант, как использовать precompiled headers легко и просто. Способ подойдёт не везде и всегда, но мне он часто помогал.

    Идём на вкладку настроек «Advanced». Выбираем все конфигурации. В поле «Forced Included File» пишем:

    Теперь «stdafx.h» автоматически будет включаться в начало ВСЕХ компилируемых файлов. PROFIT!

    Что включать в stdafx.h

    Это очень важный момент. Бездумное включение в «stdafx.h» всего подряд не только не ускорит компиляцию, но и наоборот замедлит её.

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

    Правило. Включайте в «stdafx.h» только те файлы, которые никогда не изменяются или меняются ОЧЕНЬ редко. Хорошими кандидатами являются заголовочные файлы системных и сторонних библиотек.

    Если включаете в «stdafx.h» собственные файлы из проекта, соблюдайте двойную бдительность. Включайте только те файлы, которые меняются очень-очень редко.

    Если какой-то *.h файл меняется раз в месяц, это уже слишком часто. Как правило, редко удаётся сделать все правки в h-файле с первого раза. Обычно требуется 2-3 итерации. Согласитесь, 2-3 раза полностью перекомпилировать весь проект — занятие неприятное. Плюс полная перекомпиляция потребуется всем вашим коллегам.

    Не увлекайтесь с неизменяемыми файлами. Включайте только то, что действительно часто используется. Нет смысла включать <set>, если это нужно только в двух местах. Там, где нужно, там и подключите этот заголовочный файл.

    Несколько Precompiled Headers

    Зачем в одном проекте может понадобиться несколько precompiled headers? Действительно, это нужно не часто. Но приведу пару примеров.

    В проекте используются одновременно *.c и *.cpp файлы. Для них нельзя использовать единый *.pch файл. Компилятор выдаст ошибку.

    Нужно создать два *.pch файла. Один должен получаться при компилировании C-файла (xx.c), а другой при компилировании C++-файла (yy.cpp). Соответственно, в настройках надо указать, чтобы в С-файлах использовался один precompiled header, а в С++-файлах — другой.

    Примечание. Не забудьте указать разные имена для *.pch файлов. Иначе один файл будет перетирать другой.

    Другая ситуация. Одна часть проекта использует одну большую библиотеку, а другая часть другую большую библиотеку.

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

    Логично сделать два precompiled headers и использовать их в разных участках программы. Как уже отмечалось, можно задать произвольные имена файлов, из которых генерируются *.pch файлы. Да и имя *.pch файла тоже можно изменить. Всё это, конечно, требуется делать аккуратно, но ничего сложного в использовании двух precompiled headers нет.

    Типовые ошибки при использовании Precompiled Headers

    Прочитав внимательно материал выше, вы сможете понять и устранить ошибки, связанные с stdafx.h. Но давайте ещё раз пройдёмся по типовым ошибкам компиляции и разберём их причины. Повторенье — мать ученья.

    Fatal error C1083: Cannot open precompiled header file: 'Debug\project.pch': No such file or directory

    1. Файл stdafx.cpp не компилировался и, как следствие, *.pch файл ещё не создан. Такое может быть, если, например, в начале очистить проект (Clean Solution), а потом попробовать скомпилировать один *.cpp файл (Compile Ctrl-F7). Решение: скомпилируйте проект целиком или как минимум файл stdafx.cpp.
    2. В настройках ни указано ни одного файла, из которого должен генерироваться *.pch файл. Речь идёт о ключе компиляции /Yc. Как правило, такая ситуация возникает у начинающих, которые захотели использовать precompiled headers для своего проекта. Как это сделать описано выше в разделе «Как использовать Precompiled Headers».

    Если это невозможно, то следует не использовать precompiled header для этого *.c/*.cpp файла. Уберите ключ /Yu.

    Fatal error C1853: 'project.pch' precompiled header file is from a previous version of the compiler, or the precompiled header is C++ and you are using it from C (or vice versa)

    В проекте присутствуют как C (*.c), так и C++ (*.cpp) файлы. Для них нельзя использовать единый precompiled header (*.pch файл).

    1. Отключить для всех Си-файлов использование precompiled headers. Как показывает практика, *.с файлы препроцессируются в несколько раз быстрее, чем *.cpp файлы. Если *.c файлов не очень много, то, отключив precompiled headers для них, вы ничего не потеряете
    2. Завести два precompiled headers. Первый должен создаваться из stdafx_cpp.cpp, stdafx_cpp.h. Второй из stdafx_c.c, stdafx_c.h. Соответственно, в *.c и *.cpp файлах следует использовать разные precompiled headers. Имена *.pch файлов естественно тоже должны различаться.

    Из-за precompiled header компилятор глючит

    Содержимое файла «my.h» не будет использоваться. В результате, нельзя будет использовать функции, объявленные в этом файле. Такое поведение очень сбивает программистов с толку. Они «лечат» его полным отключением precompiled headers и потом рассказывают байки о глючности Visual C++. Запомните, компилятор — это один из наиболее редко глючащих инструментов. В 99.99% случаев надо не злиться на компилятор, а искать ошибку у себя (Proof).

    Ещё один вариант — используйте Forced Included File. См. выше раздел «Life hack».

    Из-за precompiled headers проект постоянно перекомпилируется целиком

    В stdafx.h включён файл, который регулярно редактируется. Или случайно включён автогенерируемый файл.

    Внимательно проверьте содержимое файла «stdafx.h». В него должны входить только заголовочные файлы, которые не изменяются или изменяются крайне редко. Учтите, что включённые файлы могут не меняться, но внутри они ссылаются на другие изменяющиеся *.h файлы.

    Творится что-то непонятное

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

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

    Это ОЧЕНЬ редкая ситуация. Но она возможна и про неё надо знать. Я за многие годы программирования сталкивался с ней только 2-3 раза. Помогает полная перекомпиляция проекта.

    Проект, использующий precompiled headers не удаётся проверить с помощью PVS-Studio

    Это наиболее частая ситуация, с которой к нам обращаются в поддержку. Подробности изложены в документации: "Устранение неисправностей при работе PVS-Studio". Здесь опишу ситуацию кратко.

    Если решение (solution) компилируется, это вовсе не значит, что оно правильно устроено. Часто одно решение (solution) содержит множество проектов. В каждом проекте используются свои precompiled headers (имеется свой stdafx.h и stdafx.cpp).

    И, какой из stdafx.h подхватится, это интересный вопрос. Но раз программа компилируется — программисту везёт.

    К сожалению, нам сложно повторить поведение, которое возникает при использовании *.pch файла. «Честный» препроцессор работает по-другому.

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

    За подробностями вновь делаю отсылку к документации. Плюс, если что-то всё равно не ясно, мы подскажем в поддержке.

    Заключение

    Как вы увидели, ничего сложного в precompiled headers нет. Все «многочисленные глюки компилятора», с которыми сталкивается программист при их использовании, на самом деле, являются непониманием принципов работы. Надеюсь, эта статья поможет устранить непонимание.

    Precompiled headers являются очень полезным механизмом, позволяющим существенно увеличить скорость компиляции проектов.

    Статья рассчитана на людей, которые знакомятся со средой Visual Studio и пытаются компилировать в ней свои Си++-проекты. В незнакомой среде всё кажется странным и непонятным. Особенно новичков раздражает файл stdafx.h, из-за которого возникают странные ошибки во время компиляции. Очень часто всё заканчивается тем, что новичок долгое время везде старательно отключает Precompiled Headers. Чтобы помочь людям разобраться что к чему, и была написана эта статья.

    0265_stdafx_ru/image1.jpg

    Для чего нужны Precompiled Headers

    Precompiled headers предназначены для ускорения сборки проектов. Обычно программисты начинают знакомиться с Visual C++, используя крошечные проекты. На них сложно заметить выигрыш от precompiled headers. Что с ними, что без них, на глаз программа компилируется одинаковое время. Это добавляет путаницы. Человек не видит для себя пользы от этого механизма и решает, что он для специфичных задач и ему никогда не понадобится. И иногда считает так многие годы.

    На самом деле, precompiled headers весьма полезная технология. Пользу от него можно заметить, даже если в проекте всего несколько десятков файлов. Особенно выигрыш становится заметен, если используются такие тяжёлые библиотеки как boost.

    Если посмотреть *.cpp файлы в проекте, то можно заметить, что во многие включаются одни и те-же наборы заголовочных файлы. Например, <vector>, <string>, <algorithm>. В свою очередь, эти файлы включают другие заголовочные файлы и так далее.

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

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

    Как работают Precompiled Headers

    Файл, который содержит precompiled headers, имеет расширение ".pch". Имя файла обычно совпадает с названием проекта. Естественно, это и другие используемые имена можно изменить в настройках. Файл может быть весьма большим и зависит от того, как много заголовочных файлов в нём раскрыто. Например, в проекте PVS-Studio он занимает около 3 мегабайт.

    В файле "stdafx.h" находится самое интересное. Сюда нужно включить заголовочные файлы, которые будут заранее препроцессироваться. В качестве примера, вот файл stdafx.h, используемый нами в PVS-Studio (файл сокращён для статьи):

    Теперь во все файлы *.c/*.cpp следует включить "stdafx.h". Заодно стоит удалить из этих файлов заголовки, которые уже включаются с помощью "stdafx.h".

    А что делать, если используются хотя и похожие, но разные наборы заголовочных файлов? Например, такие:

    • Файл A: <vector>, <string>
    • Файл B: <vector>, <algorithm>
    • Файл C: <string>, <algorithm>

    Нужно делать отдельные precompiled headers? Так сделать можно, но не нужно.

    Достаточно сделать один precompiled header, в котором будут раскрыты <vector>, <string> и <algorithm>. Выигрыш от того, что при препроцессировании не надо читать множество файлов и вставлять их друг друга намного больше, чем потери на синтаксический анализ лишних фрагментов кода.

    Как использовать Precompiled Headers

    При создании нового проекта Wizard в Visual Studio создаёт два файла: stdafx.h и stdafx.cpp. Именно с помощью них и реализуется механизм precompiled headers.

    На самом деле, эти файлы могут называться, как угодно. Важно не название, а параметры компиляции в настройках проекта.

    В *.c/*.cpp файле можно использовать только один precompiled header. Однако, в одном проекте может присутствовать несколько разных precompiled headers. Пока будем считать, что он у нас только один.

    Итак, если вы воспользовались wizard-ом, то у вас уже есть файлы stdafx.h и stdafx.cpp. Плюс выставлены все необходимые ключи компиляции.

    Если в проекте не использовался механизм precompiled headers, то давайте рассмотрим, как его включить. Предлагаю следующую последовательность действий:

    • Во всех конфигурациях для всех *.c/*.cpp файлов включаем использование precompiled headers. Это делается на вкладке "Precompiled Header":
      • Выставляем для параметра "Precompiled Header" значение "Use (/Yu)".
      • Для параметра "Precompiled Header File" указываем "stdafx.h".
      • Для параметра "Precompiled Header Output File" указываем "$(IntDir)$(TargetName).pch".

      Вот мы и включили механизм precompiled headers. Теперь, если мы запустим компиляцию, то будет создан *.pch файл. Однако, затем компиляция остановится из-за ошибок.

      Заголовочный файл "stdafx.h" должен включаться в *.c/*.cpp файл самым первым. Обязательно! Иначе всё равно возникнут ошибки компиляции.

      Если подумать, в этом есть логика. Когда файл "stdafx.h" находится в самом начале, то можно подставить уже препроцессированный текст. Этот текст всегда одинаков и ни от чего не зависит.

      Life hack

      Есть вариант, как использовать precompiled headers легко и просто. Способ подойдёт не везде и всегда, но мне он часто помогал.

      Идём на вкладку настроек "Advanced". Выбираем все конфигурации. В поле "Forced Included File" пишем:

      Теперь "stdafx.h" автоматически будет включаться в начало ВСЕХ компилируемых файлов. PROFIT!

      Что включать в stdafx.h

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

      Все файлы, включающие "stdafx.h", зависят от его содержимого. Пусть в "stdafx.h" включен файл "X.h". Если вы поменяете хоть что-то в "X.h", это может повлечь полную перекомпиляцию всего проекта.

      Правило. Включайте в "stdafx.h" только те файлы, которые никогда не изменяются или меняются ОЧЕНЬ редко. Хорошими кандидатами являются заголовочные файлы системных и сторонних библиотек.

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

      Если какой-то *.h файл меняется раз в месяц, это уже слишком часто. Как правило, редко удаётся сделать все правки в h-файле с первого раза. Обычно требуется 2-3 итерации. Согласитесь, 2-3 раза полностью перекомпилировать весь проект - занятие неприятное. Плюс полная перекомпиляция потребуется всем вашим коллегам.

      Не увлекайтесь с неизменяемыми файлами. Включайте только то, что действительно часто используется. Нет смысла включать <set>, если это нужно только в двух местах. Там, где нужно, там и подключите этот заголовочный файл.

      Несколько Precompiled Headers

      Зачем в одном проекте может понадобиться несколько precompiled headers? Действительно, это нужно не часто. Но приведу пару примеров.

      В проекте используются одновременно *.c и *.cpp файлы. Для них нельзя использовать единый *.pch файл. Компилятор выдаст ошибку.

      Нужно создать два *.pch файла. Один должен получаться при компилировании C-файла (xx.c), а другой при компилировании C++-файла (yy.cpp). Соответственно, в настройках надо указать, чтобы в С-файлах использовался один precompiled header, а в С++-файлах - другой.

      Примечание. Не забудьте указать разные имена для *.pch файлов. Иначе один файл будет перетирать другой.

      Другая ситуация. Одна часть проекта использует одну большую библиотеку, а другая часть другую большую библиотеку.

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

      Логично сделать два precompiled headers и использовать их в разных участках программы. Как уже отмечалось, можно задать произвольные имена файлов, из которых генерируются *.pch файлы. Да и имя *.pch файла тоже можно изменить. Всё это, конечно, требуется делать аккуратно, но ничего сложного в использовании двух precompiled headers нет.

      Типовые ошибки при использовании Precompiled Headers

      Прочитав внимательно материал выше, вы сможете понять и устранить ошибки, связанные с stdafx.h. Но давайте ещё раз пройдёмся по типовым ошибкам компиляции и разберём их причины. Повторенье - мать ученья.

      Fatal error C1083: Cannot open precompiled header file: 'Debug\project.pch': No such file or directory

      Вы пытаетесь скомпилировать файл, который использует precompiled header. Но соответствующий *.pch файл отсутствует. Возможные причины:

      • Файл stdafx.cpp не компилировался и, как следствие, *.pch файл ещё не создан. Такое может быть, если, например, в начале очистить проект (Clean Solution), а потом попробовать скомпилировать один *.cpp файл (Compile Ctrl-F7). Решение: скомпилируйте проект целиком или как минимум файл stdafx.cpp.
      • В настройках ни указано ни одного файла, из которого должен генерироваться *.pch файл. Речь идёт о ключе компиляции /Yc. Как правило, такая ситуация возникает у начинающих, которые захотели использовать precompiled headers для своего проекта. Как это сделать описано выше в разделе "Как использовать Precompiled Headers".

      Если это невозможно, то следует не использовать precompiled header для этого *.c/*.cpp файла. Уберите ключ /Yu.

      Fatal error C1853: 'project.pch' precompiled header file is from a previous version of the compiler, or the precompiled header is C++ and you are using it from C (or vice versa)

      В проекте присутствуют как C (*.c), так и C++ (*.cpp) файлы. Для них нельзя использовать единый precompiled header (*.pch файл).

      • Отключить для всех Си-файлов использование precompiled headers. Как показывает практика, *.с файлы препроцессируются в несколько раз быстрее, чем *.cpp файлы. Если *.c файлов не очень много, то, отключив precompiled headers для них, вы ничего не потеряете
      • Завести два precompiled headers. Первый должен создаваться из stdafx_cpp.cpp, stdafx_cpp.h. Второй из stdafx_c.c, stdafx_c.h. Соответственно, в *.c и *.cpp файлах следует использовать разные precompiled headers. Имена *.pch файлов естественно тоже должны различаться.

      Из-за precompiled header компилятор глючит

      Содержимое файла "my.h" не будет использоваться. В результате, нельзя будет использовать функции, объявленные в этом файле. Такое поведение очень сбивает программистов с толку. Они "лечат" его полным отключением precompiled headers и потом рассказывают байки о глючности Visual C++. Запомните, компилятор - это один из наиболее редко глючащих инструментов. В 99.99% случаев надо не злиться на компилятор, а искать ошибку у себя (Proof).

      Ещё один вариант - используйте Forced Included File. См. выше раздел "Life hack".

      Из-за precompiled headers проект постоянно перекомпилируется целиком

      В stdafx.h включён файл, который регулярно редактируется. Или случайно включён автогенерируемый файл.

      Внимательно проверьте содержимое файла "stdafx.h". В него должны входить только заголовочные файлы, которые не изменяются или изменяются крайне редко. Учтите, что включённые файлы могут не меняться, но внутри они ссылаются на другие изменяющиеся *.h файлы.

      Творится что-то непонятное

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

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

      Это ОЧЕНЬ редкая ситуация. Но она возможна и про неё надо знать. Я за многие годы программирования сталкивался с ней только 2-3 раза. Помогает полная перекомпиляция проекта.

      Проект, использующий precompiled headers не удаётся проверить с помощью PVS-Studio

      Это наиболее частая ситуация, с которой к нам обращаются в поддержку. Подробности изложены в документации: "Устранение неисправностей при работе PVS-Studio". Здесь опишу ситуацию кратко.

      Если решение (solution) компилируется, это вовсе не значит, что оно правильно устроено. Часто одно решение (solution) содержит множество проектов. В каждом проекте используются свои precompiled headers (имеется свой stdafx.h и stdafx.cpp).

      И, какой из stdafx.h подхватится, это интересный вопрос. Но раз программа компилируется - программисту везёт.

      К сожалению, нам сложно повторить поведение, которое возникает при использовании *.pch файла. "Честный" препроцессор работает по-другому.

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

      За подробностями вновь делаю отсылку к документации. Плюс, если что-то всё равно не ясно, мы подскажем в поддержке.

      Заключение

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

      Precompiled headers являются очень полезным механизмом, позволяющим существенно увеличить скорость компиляции проектов.

      Читайте также: