Что делает #pragma hdrstop
If a C or C++ file contains a hdrstop pragma when compiled with either /YX or /Yc, the compiler saves the state of the compilation up to the location of the pragma. The compiled state of any code that follows the pragma is not saved.
Всё равно неясно для чего это нужно и нужно ли вообще.
Прошло более 5 лет
#1
20:14, 2 сен 2010
Собственно тоже сталкнулся с этим, Быдлер постоянно сует ее в чистых проектах, что делает сия директива? Я ее просто удаляю, но может она зачем-то нужна?
#2
20:27, 2 сен 2010
Типа PCH, тока не понятно, для чего так, если есть нормальные средства для этого.
#3
20:27, 2 сен 2010
MAMONT-92 [REBORN]
А ты загугли.
#4
20:28, 2 сен 2010
Ну на мсдне вроде все понятно написано, сохраняет все состояние компиляции до этой прагмы, если файл откомпилированного заголовка указан то он вступает в силу с только после этой прагмы, если не указан то берется дефолтный, опять же таки только после этой прагмы
#5
20:29, 2 сен 2010
кароче можно просто удалять и не волнует, так?
#6
20:29, 2 сен 2010
Wraith
> А ты загугли.
а я через гуголь и попал сюда.
#7
20:29, 2 сен 2010
MAMONT-92 [REBORN]
> кароче можно просто удалять и не волнует, так?
если стоит вначале, и выше только #include «. /precompiled.h» — Да
#8
10:57, 3 сен 2010
#pragma hdrstop — своеобразный аналог PCH — все заинклуденные хидеры до #pragma hdrstop будут прекомпилированы и закешены, если где еще в C/CPP файлах компилятор встретит ту же последовательность хидеров до #pragma hdrstop — то заюзается уже скомпиленый кеш.
- Sbtrn. Devil
- Постоялец
#9
12:20, 3 сен 2010
А в небыдлер-м$ $тудии с этой же целью заставляют инклудить stdafx.h, причём принудительно. Так-то!
#10
12:33, 3 сен 2010
Sbtrn. Devil
это был один из агрументов, почему ушел со студии, там без этой хрени ничего не компилилось.
#11
12:41, 3 сен 2010
MAMONT-92 [REBORN]
> это был один из агрументов, почему ушел со студии, там без этой хрени ничего не
> компилилось.
Бедняжка. Хоть не на линукс перешел?
Если тебя бесит то, что проект собирается быстрее, то можно отключить «юзинг прекомпаилед хиадерc» в настройках.
#12
12:43, 3 сен 2010
entryway
> Бедняжка. Хоть не на линукс перешел?
Линукс — это поделка собранная на коленке в туалете полная всевозможных костылей.
#13
12:52, 3 сен 2010
MAMONT-92 [REBORN]
> Sbtrn. Devil
> > А в небыдлер-м$ $тудии с этой же целью заставляют инклудить stdafx.h, причём принудительно. Так-то!
> это был один из агрументов, почему ушел со студии, там без этой хрени ничего не компилилось.
пф-пф-пф.
#14
12:59, 3 сен 2010
Sbtrn. Devil
> А в небыдлер-м$ $тудии с этой же целью заставляют инклудить stdafx.h, причём принудительно. Так-то!
Ну что за бред. Никто никого не заставляет, в чистом проекте вообще нет никаких stdafx.h. Я в каждом новом проекте сам создаю stdafx.h такой, какой мне нужен.
А если уж так хочется создать проект визардом и при этом убрать stdafx.h и при этом не хочется убирать галочку ответственную за это (нафига тока не ясно), то при желании парой кликов PCH отключается.
MAMONT-92 [REBORN]
> это был один из агрументов, почему ушел со студии, там без этой хрени ничего не компилилось.
Лол. Можно это просто не использовать.
Но так как эта вещь очень полезна, то не вижу смысла её не использовать.
Pragma hdrstop c что это
Опции /Yc, /Yu, директива препроцессора #pragma hdrstop, подключаемый заголовок stdafx.h — все эти функции относятся к так называемым предварительно компилируемым заголовкам (precompiled headers).
Технология precompiled headers появилась для языков программирования C/C++, когда стало ясно, что множество подключаемых заголовочных файлов (файлы с расширением *.h) слишком замедляет процесс компиляции. Поскольку подключаемые заголовки обычно изменяются намного реже, чем модули исходного кода (*.c, *.cpp), то было принято решение «предварительно» компилировать заголовки, создавая для них временные файлы, где будут сохраняться результаты компиляции. Формат этих файлов у каждого компилятора свой (у компилятора Visual Studio файлы результата компиляции заголовков имеют расширение *.pch). Файлы предварительной компиляции заголовков создаются заново только тогда, когда поменялся один из используемых заголовочных файлов, поэтому процесс компиляции проекта, когда заголовки не претерпели изменений, существенно сокращается.
Подробнее про предварительно компилируемые заголовки см. врезку «Что такое precompiled headers» в конце статьи.
[Опция /Yc]
Опция /Yc компилятора инструктирует создать файл precompiled header (с расширением *.pch), который будет хранить в себе текущее состояние компиляции.
Аргумент filename указывает заголовочный файл (*.h). Когда в опции используется этот аргумент, компилятор компилирует весь код до директивы #include этого указанного заголовочного файла, включая также и его код.
Когда опция /Yc в командной строке компилятора указана без аргумента filename, компилятор будет компилировать все подключенные заголовки до самого конца компилируемого модуля, или до точки, где в этом модуле появилась директива #pragma hdrstop [5].
Результирующий .pch файл получит то же самое имя, что и базовый исходный модуль, если Вы не укажете другое имя с помощью прагмы hdrstop или с помощью опции /Fp (см. далее). Скомпилированный файл *.pch будет использоваться при последующих компиляциях в том случае, если в строке компилятора встретится опция /Yu (см. далее).
Если опции /Ycfilename и /Yufilename встречаются в одной командной строке и обе они ссылаются прямо или косвенно на один и тот же файл, то опция /Ycfilename получает преимущество. Эта функция упрощает написание файлов makefile.
Чтобы настроить эту опцию компилятора в среде разработки Visual Studio:
1. Выберите файл *.cpp. В этом .cpp-файле должен подключаться заголовочный файл (*.h), который содержит информацию precompiled header. Настройка проекта /Yc может быть отменена на уровне этого файла.
2. Откройте окно диалога редактирования свойств проекта.
3. Откройте свойства конфигурации (Configuration Properties) -> C/C++ -> Precompiled Headers.
4. Измените свойство Precompiled Header.
5. Для установки filename измените свойство Precompiled Header File.
Примечание: есть возможность программной установки этой опции компилятора [2, 3].
Рассмотрим следующий пример на C++:
// Модуль prog.cpp, компилировался с командной строкой:
// cl /c /Ycmyapp.h prog.cpp
#include // подключаемый заголовок библиотеки классов
#include "resource.h" // подключение определений ресурсов приложения
#include "myapp.h" // подключение информации, относящейся к этому приложению
[Опция /Yu]
Опция /Yu инструктирует компилятор использовать существующий файл предварительно скомпилированных заголовков (.pch).
Аргумент filename задает имя файла заголовка, который подключен в компилируемом исходном файле с помощью директивы препроцессора #include.
Имя подключаемого файла должно быть одинаковым с опцией /Yc, с помощью которой был создан precompiled header.
Для /Yc аргумент filename задает точку, на которой останавливается процесс предварительной компиляции заголовков; компилятор будет предварительно компилировать весь код до этого filename (и код самого filename включительно), сохраняя информацию в результирующий файл precompiled header с тем же именем, что и базовый файл и расширением *.pch.
Файл *.pch должен быть создан с помощью опции /Yc (см. выше).
Компилятор обрабатывает весь код, который встретился в модуле до *.h файла как предварительно скомпилированный. Весь этот подключенный код, включая код связанного *.h файла, пропускается с использованием информации из *.pch файла, и затем компилируется весь код после filename.
В командной строки не допускается пробелов между /Yu и filename.
Когда Вы указываете опцию /Yu без имени файла, то тогда компилируемый модуль должен содержать директиву #pragma hdrstop, которая указывает имя предварительно скомпилированных заголовков (файл *.pch) заданный с помощью опции /Fp. Компилятор пропустит подключением заголовков до строки, где находится #pragma hdrstop, восстановит скомпилированное состояние из файла precompiled header, указанное директивой #pragma hdrstop, и затем компилирует только тот код, который находится за этой прагмой. Если #pragma hdrstop не указывает имя файла, то компилятор ищет имя *.pch файла, вычисленное из базового имени модуля исходного кода, заменяя расширение *.cpp на расширение *.pch. Вы также можете использовать опцию командной строки /Fp, чтобы указать другой файл *.pch.
Если Вы укажете опцию /Yu без имени файла и также не укажете #pragma hdrstop, то компилятором будет сгенерировано сообщение об ошибке, и компиляция не будет успешно завершена.
Из-за того, что файлы .pch содержат информацию о текущем рабочем окружении компьютера, а также адреса памяти, относящиеся к программе, Вы должны использовать pch-файл только на том компьютере, где он был создан.
Чтобы настроить эту опцию компилятора в среде разработки Visual Studio:
1. Укажите опцию /Yc для выбранного модуля *.cpp в проекте (как это делается, см. выше).
2. Откройте окно диалога редактирования свойств проекта.
3. Кликните на папку C/C++.
4. Кликните на страничку свойств Precompiled Headers.
5. Измените свойство Create/Use PCH Through File или свойство Create/Use Precompiled Header.
Примечание: есть возможность программной установки этой опции компилятора [2, 3].
Рассмотрим пример. Если следующий код:
#include // подключаемый заголовок библиотеки классов
#include "resource.h" // подключение определений ресурсов приложения
#include "myapp.h" // подключение информации, относящейся к этому приложению .
был скомпилирован с помощью командной строки CL /YuMYAPP.H PROG.CPP, то компилятор не будет обрабатывать эти три директивы #include, используя вместо этого предварительно скомпилированный код из MYAPP.pch, экономя тем самым время, которое иначе было бы потрачено на компиляцию этих трех заголовков (а также тех заголовочных файлов, которые могли бы в них подключаться).
Вы можете использовать опцию /Fp (указывающую имя pch файла) вместе с опцией /Yu, чтобы указать имя pch-файла, если имя этого файла отличается либо отличается от аргумента опции /Yc, или от базового имени исходного файла. Пример командной строки компилятора:
CL /YuMYAPP.H /FpMYPCH.pch PROG.CPP
Эта команда указывает имя файла предварительно скомпилированных заголовков MYPCH.pch. Компилятор использует содержимое этого файла для восстановления предварительно скомпилированного состояния всех заголовочных файлов до подключения MYAPP.h и информацию самого файла MYAPP.h включительно. Тогда компилятор будет заново компилировать только тот код, который встретится после строки директивы #include MYAPP.h.
[Опция /Fp]
Опция /Fp предоставляет путь для имени файла предварительно скомпилированных заголовков вместо использования имени пути по умолчанию.
Используйте эту опцию с опцией /Yc (создать файл предварительно скомпилированных заголовков, см. выше) или опцией /Yu (использовать файл предварительно скомпилированных заголовков, см. выше), чтобы предоставить путь и имя для precompiled header вместо использования имени пути по умолчанию. Вы можете также использовать /Fp вместе с опцией /Yc для указания использовать файл precompiled header, который отличается и от аргумента из опции /Ycfilename, и от базового имени исходного файла.
Если Вы не укажете расширение файла как часть pathname, то будет подразумеваться расширение *.pch. Если Вы укажете директорию без имени файла, то имя файла по умолчанию будет VCx0.pch, где x задает major-версию используемой системы Visual C++.
Вы также можете использовать опцию /Fp с опцией /Yu.
Чтобы настроить эту опцию компилятора в среде разработки Visual Studio:
1. Откройте окно диалога редактирования свойств проекта.
2. Кликните на папку C/C++.
3. Кликните на страничку свойств Precompiled Headers.
4. Измените свойство Precompiled Header File.
Примечание: есть возможность программной установки этой опции компилятора [4].
Рассмотрим примеры. Если Вы хотите создать файл precompiled header для своей программы, и компилируете и файлы заголовков, и исходный код, то можете указать команду компиляции следующим образом:
CL /DDEBUG /Zi /Yc /FpDPROG.PCH PROG.CPP
Следующая команда использует файл precompiled header с именем MYPCH.pch. Компилятор подразумевает, что исходный код в PROG.cpp был предварительно скомпилирован до MYAPP.h включительно, и информация о результатах этой предварительной компиляции находится в файле MYPCH.pch. Тогда компилятор будет использовать содержимое MYPCH.pch и компилировать только остальную часть файла PROG.cpp для создания obj-файла. Конечным результатом компиляции будет двоичный файл с именем PROG.exe.
CL /YuMYAPP.H /FpMYPCH.PCH PROG.CPP
[Общие замечания]
• Вставка директивы #pragma hdrstop в исходный файл, который не был скомпилирован с опциями /Yc или /Yu, не дает никакого эффекта в принципе.
• Если Вы установили опцию /Yu для файла модуля, то тогда прагма hdrstop укажет компилятору отбросить (не компилировать) все до строки, где появилась прагма hdrstop, вставив вместо этого информацию из файла precompiled header.
• Если для этого файла была установлена опция /Yc, то hdrstop означает сохранение в файл precompiled header всего скомпилированного состояния до строки, где появляется hdrstop.
Трюк использования /Yc и /Yu без опционального имени файла состоит в том, чтобы просто установить радиокнопки ‘use’ или ‘create’ и оставить поле ввода ‘through header’ пустым (или можно с той же целью отредактировать файл проекта).
Предположим, что у Вас есть 1 файл, который называется PrecompiledHeader.cpp, где подключены заголовки, которые Вы хотите добавить в файл precompiled header, и в котором в конце, после всех директив #inclide находится директива #pragma hdrstop. Тогда ТОЛЬКО ЭТОТ ОДИН ФАЙЛ компилируется с опцией /Yc. Тогда во всех других cpp-файлах с директивой #pragma hdrstop после директив #include будет использоваться precompiled header. Все эти файлы компилируются с опцией /Yu. В результате компиляция PrecompiledHeader.cpp (in this example) создаст один pch-файл, и все другие файлы проекта будут использовать этот один pch-файл.
Достоинство этого метода в том, что ни один из Ваших файлов не будет нуждаться в подключении «глобального» precompiled header — ни stdafx.h, ни какого-либо другого. Это означает, что Вы можете настроить конфигурацию сборки, которая будет выполнять компиляцию БЕЗ использования precompiled headers, где все строки #pragma hdrstop будут просто игнорироваться.
Это может быть «хорошо» потому, что позволяет иметь одну конфигурацию без предварительно скомпилированных заголовков ‘no precomp’, которая позволяет делать быстрые глобальные модификации (Вы можете поменять только один заголовок и не заставлять весь остальной мир пересобираться заново) и другие «нормальные» конфигурации, которые будут использовать precompiled headers.
Когда Вы создаете новый проект в Visual Studio, в него автоматически добавляется заголовочный файл с именем «pch.h», это так называемый файл предварительно скомпилированных заголовков, или файл precompiled headers (в ранних версиях Visual Studio, этот файл назывался «stdafx.h»). Назначение этого файла — ускорить процесс компиляции, что имеет значение для средних и больших проектов. В файл precompiled headers должно быть добавлено подключение (директивой #include) любых «стабильных» заголовочных файлов (которые никогда не изменяются, или изменяются редко), например заголовки Standard Library, такие как < vector>. Файл precompiled header компилируется только когда был изменен он сам, или был изменен какой-либо из подключаемых в нем файлов. Если Вы вносите изменения только в модули исходного кода проекта, то при сборке пропускается компиляция всех файлов, которые содержаться в precompiled header.
Опции компилятора, относящиеся к precompiled headers, начинаются с /Y. На страницах свойств проекта соответствующие опции находятся в разделе Configuration Properties -> C/C++ -> Precompiled Headers. Вы можете здесь выбрать не использовать precompiled headers, и также можете указать специальное имя заголовка и путь имя и путь результата компиляции precompiled headers.
Пользовательский предварительно скомпилированный код. Для больших проектов, для которых важно экономить время на сборку, Вы можете решить создать свои собственные предварительно скомпилированные файлы (Precompile Source Code). Компиляторы Microsoft C и C++ предоставляют опции для предварительной компиляции любого кода C или C++, включая inline-код. Используя эту функцию повышения производительности, Вы можете скомпилировать стабильное тело кода, сохранить скомпилированное состояние в файл, и при последующих компиляциях комбинировать предварительно скомпилированный код с кодом, находящимся в текущей разработке. Каждая последующая компиляция будет происходить быстрее, потому что не нужно заново компилировать предварительно скомпилированный код.
[Когда следует использовать Precompile Source Code]
Precompiled-код полезен во время разработки для снижения времени компиляции, особенно в следующих ситуациях:
• Всегда есть большой объем кода, который редко изменяется.
• Ваша программа состоит из нескольких модулей, и все они используют стандартный набор подключаемых файлов и одинаковые опции компиляции. В этом случае все подключаемые файлы проекта можно предварительно скомпилировать в один precompiled header.
Первая компиляция — в которой будет создан файл precompiled header (PCH) — будет происходить дольше, чем последующие компиляции, которые будут подключать precompiled-код.
Вы можете предварительно компилировать как программы на языке C, таки и программы на языке C++. В программировании C++ общей практикой является выделение информации интерфейса класса в заголовочные файлы. Эти заголовочные файлы могут быть позже подключены в программы, которые используют эти классы. Путем предварительной компиляции этих заголовков Вы можете снизить время, которое тратится на компиляцию программы.
Примечание: хотя Вы можете использовать только один файл precompiled header (с расширением *.pch) на исходный файл, в проекте также можно использовать несколько pch-файлов.
[Два выбора для предварительного компилирования кода]
В среде Visual C++ Вы можете предварительно компилировать любой код C или C++; Вы не ограничены только заголовочными файлами.
Прекомпиляция требует предварительного планирования, однако часто эта техника позволяет значительно ускорить сборки, если предварительно компилируется код, отличающийся от простых заголовочных файлов.
Используйте precompiled-код, когда знаете, что Ваши исходные файлы используют общий набор заголовочных файлов, однако не подключают их в одинаковом порядке, или когда Вы хотите добавить исходный код в свою предварительную компиляцию.
Опциями precompiled-header служат /Yc (Create Precompiled Header File) и /Yu (Use Precompiled Header File). Используйте /Yc для создания precompiled header. Когда используется опциональная директива #pragma hdrstop, опция /Yc позволит Вам предварительно компилировать как заголовочные файлы, так и исходный код. Выберите /Yu для использования существующего файла precompiled header в имеющейся компиляции. Вы можете также использовать опцию /Fp с опциями /Yc и /Yu, чтобы предоставить альтернативное имя для precompiled header.
[Правила целостности Precompiled Header]
Поскольку PCH-файлы содержат информацию о рабочем окружении (компьютере разработчика, в том числе информацию адресов памяти программы), Вы должны использовать PCH-файл только на том компьютере, где он было создан.
Правила целостности для каждого файла. Опция /Yu компилятора позволяет Вам указать, какой PCH-файл использовать.
Когда Вы используете PCH-файл, компилятор подразумевает, что используется одно и то же рабочее окружение компиляции — используются целостные опции компилятора, прагмы, версия и т. д. — которые дают тот же эффект компиляции, который был при создании PCH-файла, если Вы не укажете нечто другое. Если компилятор определил какое-либо нарушение целостности, то он выдаст предупреждение и покажет по мере возможности, в чем было это нарушение. Такие предупреждения не обязательно показывают проблему в PCH-файле; они просто сообщают Вам о возможном возникновении конфликтов. Далее будут описаны требования целостности для PCH-файлов.
Целостность опций компилятора. Следующие опции компилятора могут привести к предупреждениям нарушения целостности при использовании PCH-файла:
• Макросы, созданные с помощью опции препроцессора (/D) должны быть одинаковыми, как были в момент компиляции создания PCH-файла и в текущей компиляции. Состояние констант #define не проверяется, однако в случае их изменений возможны непредсказуемые результаты.
• PCH-файлы не работают с опциями /E и /EP.
• PCH-файлы должны быть созданы либо с опцией Generate Browse Info (/FR), либо с опцией Exclude Local Variables (/Fr) перед последующими компиляциями, которые могут использовать PCH-файл и эти опции.
Совместимость «C 7.0» (/Z7). Если действует опция «C 7.0-Compatible» (/Z7) при создании PCH-файла, то последующие компиляции, которые используют этот PCH-файл, могут использовать отладочную информацию.
Если опция «C 7.0-Compatible» (/Z7) не применялась при создании PCH-файла, то последующие компиляции, использующие этот PCH-файл вместе с опцией /Z7, будут приводить к генерации предупреждения. Отладочная информация, помещенная в текущий .obj файл, и локальные символы в файле PCH не будут доступны для отладчика.
Целостность путей подключения. PCH-файл не содержит информацию о путях поиска подключаемых файлов (Include Path), которая действовала в момент создания этого PCH-файла. Когда Вы используете PCH-файл, компилятор всегда использует пути поиска подключаемых файлов, настроенные для текущей компиляции.
Целостность исходных файлов. Кода Вы указываете использовать Precompiled Header File (опцию /Yu), компилятор игнорирует все директивы препроцессора (включая директивы #pragma) которые появляются в исходном коде, подвергшемся предварительной компиляции. Способ компиляции, заданный этими директивами препроцессора, должен быть таким же, как использовался в момент создания Precompiled Header File (с опцией /Yc).
Целостность #pragma. Директивы #pragma, обработанные в момент создания PCH-файла, обычно влияют на файл, который будет использовать этот PCH-файл в последующих компиляциях. Прагмы comment и message не влияют на остальную часть компиляции.
Следующие директивы #pragma влияют только на код в файле PCH; они не влияют на код, который использует этот PCH-файл:
comment
page
subtitle
linesize
pagesize
title
message
skip
Следующие директивы #pragma сохраняются как часть precompiled header, и влияют на компиляцию, которая использует этот precompiled header:
alloc_text
include_alias
pack
auto_inline
init_seg
pointers_to_members
check_stack
inline_depth
setlocale
code_seg
inline_recursion
vtordisp
data_seg
intrinsic
warning
function
optimize
[Правила целостности для /Yc и /Yu]
Когда Вы используете precompiled header с опциями /Yc или /Yu, компилятор сравнивает текущее рабочее окружение компиляции и то окружение, которое существовало в момент создания PCH-файла. Убедитесь, что для текущей компиляции задаете одинаковое, целостное окружение, совпадающее с предыдущим (с использованием одинаковых опций компилятора, прагм и т. д.). Если компилятор обнаружит нарушение целостности, то он выдаст предупреждение, и если есть возможность, то укажет на его причину. Подобные предупреждения необязательно показывают проблему в файле PCH, они просто предупреждают о возможном возникновении конфликтов. В таблице ниже приведены требования целостности к опциям компилятора.
| Опция | Имена | Правило |
| /D | Определение констант и макросов | Должно быть одинаковым между компиляцией с созданным precompiled header и текущей компиляцией. Состояние определяемых констант не проверяется, однако могут быть непредсказуемые результаты, если Ваши файлы зависят от значений измененных констант. |
| /E или /EP | Копирование вывода препроцессора в стандартный вывод (stdout) | Технология precompiled headers не работает с опцией /E или /EP. |
| /Fr или /FR | Генерация информации Microsoft Source Browser | Чтобы опции /Fr и /FR были допустимы с опцией /Yu, они должны действовать в момент создания precompiled header. Последующие компиляции, которые используют precompiled header, также генерируют информацию Source Browser. Эта информация помещается в один файл *.sbr, и к нему обращаются другие файлы таким же способом, как и к информации CodeView. Вы не можете изменить место размещения информации Source Browser. |
| /GA, /GD, /GE, /Gw или /GW | Опции протокола Windows | Должны быть одинаковыми между компиляцией, создавшей precompiled header, и текущей компиляцией. Если эти опции различаются, то будет выведено предупреждающее сообщение. |
| /Zi | Генерация полной отладочной информации | Если эти опции действуют в момент создания precompiled header, то последующие компиляции, которые используют эту предкомпиляцию, могут использовать отладочную информацию. Если /Zi не действовала, когда был создан precompiled header, то последующие компиляции, которые используют опцию /Zi, приведут к выводу предупреждения компилятора. Отладочная информация помещается в текущий объектный файл, и локальные символы, определенные в precompiled header, будут недоступны для отладчика. |
Примечание: технология precompiled предназначена только для использования в исходном коде C и C++.
[Использование Precompiled Headers в проекте]
В предыдущих секциях обзорно рассматривалось управление precompiled headers: опции /Yc, /Yu и /Fp и прагма hdrstop. В этой секции описывается метод создания опций precompiled header для проекта вручную. В конце будет приведет пример makefile и кода, который им обслуживается.
Другой способ использования вручную созданных опций в проекте — изучить один из файлов makefile, находящиеся в директории MFC\SRC, которая создается в процессе установки по умолчанию Visual C++. Эти файлы makefile дают простой способ применения precompiled header, представленный в этой секции, однако он больше использует макросы Microsoft Program Maintenance Utility (NMAKE), и дает больше возможностей по управлению процессом сборки.
PCH-файлы в процессе сборки. Кодовая база программного проекта обычно содержится в нескольких модулях исходного кода на языке C или C++, объектных файлах, библиотеках и заголовочных файлах. Обычно файлы makefile координируют комбинацию этих элементов в исполняемый файл (*.exe). На следующем рисунке показана структура makefile, который использует файл precompiled header. Имена макроса NMAKE и имена файлов в этой диаграмме соответствуют коду примера, который находится в Примере 1 Makefile для PCH и Примере 2 кода для PCH.
Структура Makefile, который использует файл Precompiled Header:
Рисунок использует три диаграммных устройства, чтобы показать поток процесса сборки. Прямоугольники с именами представляют каждый файл или макрос; дерево макросов представляют один или большее количество файлов. Затененные области представляют каждое действие по компиляции или линковке. Стрелки показывают, какие файлы и макросы комбинируются во время процесса компиляции или линковки.
В начале верхней части диаграммы, STABLEHDRS и BOUNDRY это оба макросы NMAKE, в которых Вы перечисляете файлы, которые вряд ли нуждаются в перекомпиляции. Эти файлы компилируются строкой команды
CL /c /W3 /Yc$(BOUNDRY) applib.cpp myapp.cpp
только если файл precompiled header (STABLE.pch) не существует, или если Вы сделали изменения в файлы, перечисленные в этих двух макросах. В любом случае файл precompiled header будет содержать код только из файлов, перечисленных в макросе STABLEHDRS. Список последнего файла, который Вы хотите предварительно скомпилировать, находится в макросе BOUNDRY.
Файлы, перечисленные в этих макросах, могут быть либо заголовочными файлами (*.h), либо исходным кодом C или C++ (один PCH-файл не может использоваться с модулями обоих языков C и C++). Обратите внимание, что Вы можете использовать макрос hdrstop для остановки предварительной компиляции в некоторой точке файла BOUNDRY.
Ниже на диаграмме APPLIB.obj представляет поддержку кода, используемого в Вашем конечном приложении. Он создается из APPLIB.cpp, а также из файлов, перечисленные в макросе UNSTABLEHDRS, и предварительно скомпилированного кода из precompiled header.
MYAPP.obj представляет конечное приложение. Он создается из MYAPP.cpp, файлов, перечисленных в макросе UNSTABLEHDRS, и предварительно скомпилированного кода из precompiled header.
И наконец, исполняемый файл (MYAPP.EXE) создается линковкой файлов, перечисленных в макросе OBJS (файлы APPLIB.obj и MYAPP.obj).
Пример 1 Makefile для PCH. Следующий makefile использует макросы и структуры команд управления потоком !IF, !ELSE, !ENDIF, чтобы упростить адаптацию для Вашего проекта.
Содержимое файла makefile:
# Makefile: иллюстрация эффективного использования precompiled
# headers в проекте
# Usage: NMAKE option
# option: DEBUG=[0|1]
# (если DEBUG не определен, то это эквивалентно DEBUG=0)
OBJS = myapp.obj applib.obj
# Список всех стабильных (редко изменяемых) файлов в макросе STABLEHDRS:
STABLEHDRS = stable.h another.h
# Список конечного заголовочного файла, который здесь прекомпилируется:
BOUNDRY = stable.h
# Список изменяемых заголовков (хедеры, находящиеся в разработке):
UNSTABLEHDRS = unstable.h
# Список всех опций компилятора для обоих версий
# приложения, debug и final:
CLFLAGS = /c /W3
# Список всех опций линкера для обоих версий приложения, debug и final:
LINKFLAGS = /NOD /ONERROR:NOEXE
!IF "$(DEBUG)" == "1"
CLFLAGS = /D_DEBUG $(CLFLAGS) /Od /Zi /f
LINKFLAGS = $(LINKFLAGS) /COD
LIBS = slibce
!ELSE
CLFLAGS = $(CLFLAGS) /Oselg /Gs
LINKFLAGS = $(LINKFLAGS)
LIBS = slibce
!ENDIF
myapp.exe: $(OBJS) link $(LINKFLAGS) @$(OBJS), myapp, NUL, $(LIBS), NUL;# Компиляция myappmyapp.obj : myapp.cpp $(UNSTABLEHDRS) stable.pch $(CPP) $(CLFLAGS) /Yu$(BOUNDRY) myapp.cpp# Компиляция applib applib.obj : applib.cpp $(UNSTABLEHDRS) stable.pch $(CPP) $(CLFLAGS) /Yu$(BOUNDRY) applib.cpp# Компиляция заголовков stable.pch : $(STABLEHDRS) $(CPP) $(CLFLAGS) /Yc$(BOUNDRY) applib.cpp myapp.cppКроме макросов STABLEHDRS, BOUNDRY и UNSTABLEHDRS, показанных на рисунке "Структура Makefile, который использует файл Precompiled Header", используемых в PCH-файлах в процессе сборки, этот makefile предоставляет макросы CLFLAGS и LINKFLAGS. Вы должны использовать эти макросы, чтобы указать списки опций компилятора и линкера, применяемых при сборке к отладочной или финальной версии исполняемого файла приложения. Здесь есть также макрос LIBS, где Вы перечисляете библиотеки, требуемые для проекта.
Файл makefile также использует !IF, !ELSE, !ENDIF чтобы выяснить, определен ли символ DEBUG в командной строке NMAKE:
Эта функция делает возможной использовать один и тот же makefile как для отладочной, так и для финальной версии программы — для финальной версии используйте DEBUG=0. Если ничего не указывать, то подразумевается финальная версия, следующие строки команд эквивалентны:
NMAKE
NMAKE DEBUG=0Для дополнительной информации по файлам makefile см. руководство NMAKE [6]. Также см. описание опций компилятора [7] и линкера [8].
Пример 2 кода для PCH. Следующие файлы исходного кода используются в примере makefile, описанном выше. Обратите внимание, что комментарии содержат важную информацию.
Заголовочный файл another.h:
// ANOTHER.H: содержит интерфейс для кода, который скорее// всего будет редко изменяться.#ifndef __ANOTHER_H#define __ANOTHER_HС++ Builder: как ускорить компиляцию с помощью предкомпилированных заголовков
Вместо вступления сразу приведу пример. Полная сборка (build) проекта, содержащего около 170 cpp-модулей, при использовании предкомпилированных заголовков происходит за 811 секунд, при этом число обработанных компилятором строк составляет 1,808,780. При компиляции того же проекта без использования предкомпилированных заголовков, время сборки составляет 2399 секунд, а число строк, обработанных компилятором - 45,261,820. Впечатляет, не так ли? Плата за это ускорение, в принципе не велика - предкомпилированный образ, размер которого около 40 Мб.
При компиляции исходных текстов, компилятор должен обработать все *.cpp файлы проекта и все включенные в них *.h - файлы. При этом обрабатываются как пользовательские заголовочные файлы, так и стандартные, такие как vcl.h или Word2k.h. Количество кода, находящегося в стандартных заголовках может быть очень большим, например размер файла Word2k.h превышает 5 Мб, в нем больше 130 000 строк кода.
Так как содержимое стандартных заголовков не изменяется, то их компиляция при каждой сборке проекта является напрасной тратой времени. Предкомпилированные заголовки помогают решить эту проблему - стандартные файлы компилируются один раз, а затем используется скомпилированный двоичный образ.
Принцип действия предкомпилированных заголовков
Для управления предкомпилированными предназначена директива компилятора #pragma hdrstop. Все заголовочные файлы, включенные до этой директивы, помещаются в один образ, например:
#include #include #pragma hdrstopТакая последовательность создаст образ, содержащий скомпилированные vcl.h и string. Этот образ будет использован для другого cpp-файла, если в нем до директивы hdrstop будут включены те же файлы, в том же порядке. Обращу внимание, что важен не только состав, но и порядок следования заголовков - даже если следующий cpp-файл включает те же заголовки, но сначала указан string, а потом vcl.h, то для этого cpp-файла будет создан новый образ.
Таким образом, для повторного использования предкомпилированного заголовка необходимо выполнение двух условий:
- состав включенный файлов до директивы hdrstop должен быть тем же
- последовательность включения файлов до директивы hdrstop должна быть той же
Сократить затраты на компиляцию стандартных заголовков до минимума можно только в том случае, если скомпилировать один образ, содержащий все стандартные заголовки, необходимые для проекта. Для этого нужно, чтобы:
- ВСЕ cpp-файлы проекта имели одинаковый блок включений до директивы hdrstop
- в этот блок должны входить ВСЕ стандартные заголовочные файлы, необходимые для проекта
Выполнить эти условия достаточно просто, для этого в начало каждого cpp-файла необходимо поместить следующие строки:
#include #pragma hdrstop
где pch.h - файл, содержащий включения всех стандартных заголовков:
#ifndef PCH_H #define PCH_H #define INC_VCLDB_HEADERS #define INC_VCLEXT_HEADERS #include #include #include #include #include . #endif
Полный текст моей версии этого файла приведен в конце статьи. На h-файлы, входящие в предкомпилированный образ, накладывается ограничение - в них не должно быть инициализированных данных, например, в math.hpp есть строки:
static const Extended NaN = 0.0 / 0.0; static const Extended Infinity = 1.0 / 0.0;
Из-за наличия этих констант включить math.hpp в файл pch.h нельзя.
Кстати, С++ Builder при добавлении новых модулей в проект реализует описанную стратегию управления предкомпилированными заголовками. Например, при создании нового приложения, файл Unit1.cpp будет таким:
#include #pragma hdrstop #include "Unit1.h" .
Если посмотреть на текст vcl.h, то можно увидеть, что он является оболочкой для включения большого числа других стандартных заголовочных файлов.
Управлять составом включаемых в vcl.h заголовков можно с помощью специальных символов (INC_VCLDB_HEADERS, INC_VCLEXT_HEADERS и др.). В моей версии pch.h эти символы определяются с помощью #define до включения vcl.h, что приводит к увеличению числа включаемых файлов.
Как в существующем проекте перейти к использованию предкомпилированных заголовков
Даже в большом проекте перейти к использованию предкомпилированных заголовков достаточно просто.
В свойствах проекта нужно включить кэширование предкомпилированных заголовков и рекомендуется указать "персональный" файл, в котором будет храниться образ предкомпилированных заголовков: Project - Options - закладка Compiler, группа "Pre-compiled headers". Тут должно быть выбрано "Cache pre-compiled headers", а в поле "File Name" нужно ввести "pch.csm". При такой настройке образ с предкомпилированными заголовками будет находится в папке с проектом, в файле pch.csm.
После этого в начало каждого cpp-модуля необходимо вставить 2 строки:
#include "pch.h" #pragma hdrstop
Все ранее включенные заголовочные файлы остаются на своих местах, их удалять не надо. Например:
#include "pch.h" // включает vcl.h, string и т.д. #pragma hdrstop #include #include .
Так как во всех стандартных заголовках применяются стражи повторного включения, то повторное их упоминание не влечет за собой повторного включения.
В принципе, при использовании pch.h, техническая потребность во включении стандартных заголовков исчезает. Однако, полезно все же указывать все необходимые для каждого конкретного модуля заголовки ниже директивы #pragma hdrstop. Во-первых, это в некоторой степени документирует модуль - по включаемым файлам можно судить, какими возможностями пользуется этот модуль. Во-вторых, это облегчает повторное использование модуля в других проектах, в которых либо не используется pch.h, либо его содержимое может быть другим.
Теоретически можно еще больше повысить эффективность компиляции, если включить в pch.h не только стандартные, но и все пользовательские заголовочные файлы. Практически, так как пользовательские заголовки меняются достаточно часто, это может повлечь за собой частую перекомпиляцию pch.h, что негативно скажется на времени компиляции. Кроме того, пользовательские заголовки обычно не бывают очень большими и компилируются очень быстро. Поэтому включать их pch.h не целесообразно.
Как проверить, что предкомпилированные заголовки используются эффективно
При добавлении в проект новых файлов нужно не забывать включать в них pch.h, иначе для них не будет использован общий предкомпилированный образ. Такая же ситуация может возникнуть, если в каком-то модуле включаются стандартные заголовки, которые не вошли в pch.h. Для того, чтобы отследить такие файлы, есть несколько способов:
- визуальное наблюдение за процессом компиляции. Обычно, число строк компилируемых в одном файле не должно превышать 10000-15000 строк
- если для проекта выбран индивидуальный файл для хранения образа предкомпилированных заголовков и этот файл называется pch.csm, то нужно обратить внимание на наличие вспомогательных файлов с именами pch.#00, pch.#01 и т.д. Если для всех файлов проекта используется один и тот же предкомпилированный образ, то вспомогательный файл должен быть только один - pch.#00. Если таких файлов больше, это значит что для каких-то cpp-модулей создаются дополнительные образы.
#ifndef PCH_H #define PCH_H #define INC_VCLDB_HEADERS #define INC_VCLEXT_HEADERS #include /* Все, что подключается предыдущими 3-мя строчками // Core (minimal) Delphi RTL headers #include #include #include #include #include #include // Core (minimal) VCL headers #if defined(INC_VCL) #include #include #include #include #include #include // VCL Database related headers #if defined(INC_VCLDB_HEADERS) #include #include #include #endif // INC_VCLDB_HEADERS // Full set of VCL headers #if defined(INC_VCLEXT_HEADERS) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif // INC_VCLEXT_HEADERS #endif // INC_VCL */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //нестандартные компоненты RxLib и EhLib #include "CURREDIT.hpp" #include "RXSpin.hpp" #include "RxCalc.hpp" #include "RxCombos.hpp" #include "APPUTILS.hpp" #include "RXDBCtrl.hpp" #include "Placemnt.hpp" #include "DBGridEh.hpp" #endif
О вышеизложенном
Если по поводу изложенного материала есть вопросы и замечания, то я готов их принять - пишите seb.goo@gmail.com.
#pragma hdrstop
This directive terminates the list of header files eligible for precompilation. You can use it to reduce the amount of disk space used by precompiled headers.
Precompiled header files can be shared between the source files of your project only if the #include directives before #pragma hdrstop are identical. Therefore, you get the best compiler performance if you include common header files of your project before the #pragma hdrstop , and specific ones after it. Make sure the #include directives before the #pragma hdrstop are identical in all the source files, or that there are only very few variations.
The integrated development environment generates code to enhance precompiled header performance. For example, after a New Application, source file "Unit1.cpp" will look like this (comments added):
#include // common header file #pragma hdrstop // terminate list here
Use this pragma directive only in source files. The pragma has no effect when it is used in a header file.