Как включить stdafx.h из корневого каталога?

Как использовать 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».
  • Создаём и добавляем в проект файл stdafx.h. В дальнейшем, в него мы будем включать те заголовочные файлы, которые хотим заранее препроцессировать.
  • Создаём и добавляем в проект файл stdafx.cpp. В нём одна единственная строка: #include «stdafx.h».
  • Во всех конфигурациях меняем настройки для файла stdafx.cpp. Выставляем для параметра «Precompiled Header» значение «Create (/Yc)».

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

Для всех *.c/*.cpp файлов мы указали, что они должны использовать precompiled headers. Этого мало. Теперь в каждый из файлов нужно добавить #include «stdafx.h».

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

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

Представьте ситуацию, если бы мы могли включить до «stdafx.h» ещё какой-то файл. А в этом файле возьмём и напишем: #define bool char. Возникает неоднозначность. Мы меняем содержимое всех файлов, в которых упоминается «bool». Теперь просто так нельзя взять и подставить заранее препроцессированный текст. Ломается весь механизм «precompiled headers». Думаю, это одна из причин, почему «stdafx.h» должен быть расположен в начале. Возможно, есть и другие.

What to include into stdafx.h

This is a very important question. Mindlessly including every single header into “stdafx.h” will slow down the compilation process instead of speeding it up.

All the files that include “stdafx.h” depend on its contents. Suppose “stdafx.h” includes the file “X.h”. Changing “X.h” just a little bit may cause complete recompilation of the whole project.

Important rule. Make sure your “stdafx.h” file includes only those files that never, or VERY rarely change. The best candidates are headers from system and third-party libraries.

If you include you own project files into “stdafx.h”, be especially careful. Include only those files that change very, very rarely.

If any of the *.h files change once a month, it’s too frequent. In most cases, it takes you more than once to make all the necessary edits in an h-file – usually 2 or 3 times. Completely recompiling the entire project 2 or 3 times is quite an unpleasant thing, isn’t it? Besides, all your colleagues will need to do the same.

But don’t be too fanatical about non-changing files. Include only those headers which you use really often. Including <set> won’t make sense, if you need it in just a couple of files. Instead, simply include this file where needed.

Several precompiled headers

For what may we need several precompiled headers, in one project? Well, it’s a pretty rare situation indeed. But here are couple of examples.

Imagine the project is using both *.c and *.cpp files together. You can’t use a shared *.pch file for them – the compiler will generate an error.

You have to create two *.pch files. One of them is created after compiling the C-file (xx.c), the other after compiling the C++-file (yy.cpp). Accordingly, you should specify in the settings to use one precompiled header for C-files, and another for C++-files.

Note: Don’t forget to set different names for these two *.pch files. Otherwise they will be replacing each other.

Here’s another situation:
One part of the project uses one large library, while the other part uses another large library.

Naturally, different parts of the project should not know about both libraries: there may be (unlucky) overlapping of entities’ names in different libraries.

It is logical to create two precompiled headers, and use them in different parts of the program. As we have already mentioned, you may use any names you like for the files the *.pch files are generated from. Well, even the name of the *.pch file can be changed too. It should all be done very carefully of course, but there’s nothing especially difficult about using two precompiled headers.

The purpose of precompiled headers

Precompiled headers are intended to speed up project builds. When getting started with Visual C++, programmers usually try it on very small projects that cannot show the performance gain from using precompiled headers. Both with and without them, the program seems to take the same time to compile. This is just what confuses the user; he doesn’t see any use in this option, and concludes that it is needed for some specific tasks and he will never need it. This delusion may last for years.

Precompiled headers are actually a very useful technology. The benefit can be seen even with a project of just a few dozen files. Using such heavy libraries as a boost will make the performance gain especially evident.

If you examine the *.cpp files in your project, you will notice that many of them include the same sets of headers, for example <vector>, <string>, <algorithm>. These headers, in their turn, include other headers, and so on.

All this results in the compiler’s preprocessor doing the same work again and again – it must read the same files many times, insert them into each other, process #ifdef, and expand macros. Because of this, the same operations are repeated a huge number of times.

The amount of work the preprocessor has to do during project compilation can be greatly reduced. The idea is to preprocess a group of files in advance, and then simply insert already prepared text fragments where necessary.

It actually includes a few more steps; instead of simple text, you can store more highly processed information. We don’t know how exactly it is all implemented in Visual C++, but I know that, for instance, you can store text already split into lexemes. This will speed up the compilation process even more.

Life hack

Прописывать #include «stdafx.h» во все *.c/*.cpp файлы достаточно утомительно и не интересно. Дополнительно получится ревизия в системе контроля версий, где будет изменено огромное количество файлов. Нехорошо.

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

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

Можно не прописывать во все файлы #include «stdafx.h», а воспользоваться механизмом «Forced Included File».

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

StdAfx.h;%(ForcedIncludeFiles)

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

Больше не потребуется писать #include «stdafx.h» в начале всех *.c/*.cpp файлов. Компилятор сделает это сам.

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

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

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

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

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

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

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

Класс фреймворкаПравить

Создайте заголовок Framework.h и напишите этот код:

#pragma once

#include "Window.h"
#include "Render.h"
#include "InputMgr.h"
#include "Log.h"

namespace D3D11Framework
{
//------------------------------------------------------------------

	class Framework
	{
	public:
		Framework();
		~Framework();

		bool Init();
		void Run();
		void Close();

		void SetRender(Render *render){m_render = render;}
		void AddInputListener(InputListener *listener);
	protected:	
		bool m_frame();	

		Window *m_wnd;
		Render *m_render;
		InputMgr *m_input;
		Log m_log;
		bool m_init;		// если было инициализировано
	};

//------------------------------------------------------------------
}

Класс Framework взаимодействует с другими классами и поэтому содержит их экземпляры. Init() – инициализирует нашу библиотеку. Run() производит выполнение библиотеки. Close(), завершает работу библиотеки. SetRender() позволяет нам передать нашего наследника класса Render. AddInputListener() добавляет слушателя для ввода. m_frame() выполняет работу одного кадра.

Теперь создайте Framework.cpp и напишите следующий код:

#include "stdafx.h"
#include "Framework.h"
#include "macros.h"
#include "Log.h"

namespace D3D11Framework
{
//------------------------------------------------------------------

	Framework::Framework() :
		m_wnd(nullptr),
		m_render(nullptr),
		m_input(nullptr),
		m_init(false)
	{
	}

	Framework::~Framework()
	{
	}

	void Framework::AddInputListener(InputListener *listener)
	{
		if (m_input)
			m_input->AddListener(listener);
	}

	void Framework::Run()
	{
		if (m_init)
			while(m_frame());
	}

	bool Framework::Init()
	{
		m_wnd = new Window();
		m_input = new InputMgr();

		if ( !m_wnd || !m_input )
		{
			Log::Get()->Err("Не удалось выделить память");
			return false;
		}

		m_input->Init();

		// Создаем значения настроек по умолчанию. В одном из будущих уроков мы вернемся к этому
		DescWindow desc;			
		if ( !m_wnd->Create(desc) )
		{
			Log::Get()->Err("Не удалось создать окно");
			return false;
		}
		m_wnd->SetInputMgr(m_input);

		if ( !m_render->Init(m_wnd->GetHWND()) )
		{
			Log::Get()->Err("Не удалось создать рендер");
			return false;
		}

		m_init = true;
		return true;
	}

	bool Framework::m_frame()
	{
		// обрабатываем события окна
		m_wnd->RunEvent();
		// если окно неактивно - завершаем кадр
		if (!m_wnd->IsActive())
			return true;

		// если окно было закрыто, завершаем работу движка
		if (m_wnd->IsExit())
			return false;

		// если окно изменило размер
		if (m_wnd->IsResize())
		{
		}

		if (!m_render->Draw())
			return false;

		return true;
	}

	void Framework::Close()
	{
		m_init = false;
		_CLOSE(m_render);
		_CLOSE(m_wnd);
		_CLOSE(m_input);
	}

//------------------------------------------------------------------
}

Компилируйте. Если все успешно, поспешу порадовать вас – на этом мы закончили создание фреймворка для наших уроков. Но не спешите закрывать код, мы еще не протестировали его.И еще, создайте заголовок D3D11_Framework.h и в нем следующий код:

#pragma once

#include "macros.h"
#include "stdafx.h"
#include "Framework.h"
#include "InputListener.h"
#include "Render.h"

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

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

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

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

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

Сообщение говорит само за себя, если его прочитать. Файл компилируется с ключом /Yu. Это значит, что следует использовать precompiled header. Но в файл не включён «stdafx.h».

Нужно вписать в файл #include «stdafx.h».

Если это невозможно, то следует не использовать 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_cpp.h. Соответственно, в *.c и *.cpp файлах следует использовать разные precompiled headers. Имена *.pch файлов естественно тоже должны различаться.

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

Скорее всего, что-то сделано не так. Например, #include «stdafx.h» расположен не в самом начале.

Рассмотрим пример:

Этот код не скомпилируется. Компилятор выдаст на первый взгляд странное сообщение об ошибке:

Компилятор считает, что все, что указано до строчки #include «stdafx.h» (включительно), является precompiled header. При компиляции файла компилятор заменит все, что до #include «stdafx.h» на текст из *.pch файла. В результате теряется строчка «int A = 10».

Правильный вариант:

Ещё пример:

Чтобы таких ситуаций не было, ВСЕГДА пишите #include «stdafx.h» в самом начале файла. Комментарии перед #include «stdafx.h» можно оставить. Они всё равно никак не участвуют в компиляции.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

What to include into stdafx.h

This is a very important question. Mindlessly including every single header into «stdafx.h» will slow down the compilation process instead of speeding it up.

All the files that include «stdafx.h» depend on its contents. Suppose «stdafx.h» includes the file «X.h». Changing «X.h» just a little bit may cause complete recompilation of the whole project.

Important rule. Make sure your «stdafx.h» file includes only those files that never, or VERY rarely change. The best candidates are headers from system and third-party libraries.

If you include you own project files into «stdafx.h», be especially careful. Include only those files that change very, very rarely.

If any of the *.h files change once a month, it’s too frequent. In most cases, it takes you more than once to make all the necessary edits in an h-file — usually 2 or 3 times. Completely recompiling the entire project 2 or 3 times is quite an unpleasant thing, isn’t it? Besides, all your colleagues will need to do the same.

But don’t be too fanatical about non-changing files. Include only those headers which you use really often. Including <set> won’t make sense, if you need it in just a couple of files. Instead, simply include this file where needed.

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

Для начала давайте напишем заголовок в котором будем подключать стороний код. Сделайте его предкомпилированным (если не знаете что это такое, читайте здесь). У меня этот заголовок имеет стандартное имя — stdafx.h

Напишите в нем следующий код:

#pragma once

#include <clocale>
#include <ctime>

#include <string>
#include <list>

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <d3d11.h>
#include <d3dx11.h>

#pragma comment(lib, "d3d11.lib")
#ifdef _DEBUG
#	pragma comment(lib, "d3dx11d.lib")
#else
#	pragma comment(lib, "d3dx11.lib")
#endif

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

Далее мы подключаем все нужные внешние заголовки – стандартные, STL, WinAPI и DX11.

WIN32_LEAN_AND_MEAN указывает что из заголовка windows.h нужно брать минимум кода, за счет этого уменьшается размер нашего файла и ускоряется компиляция.

Теперь по поводу #pragma comment. С помощью этой директивы мы указываем, что к проекту нужно будет прилинковать d3d11.lib и d3dx11.lib (d3dx11d.lib). Если ваш компилятор не поддерживает такой директивы, то укажите библиотеки через настройки проекта.

PCH-файлы в процессе построенияPCH Files in the Build Process

База кода проекта программного обеспечения обычно содержится в нескольких исходных файлах C или C++, объектных файлах, библиотеках и файлах заголовков.The code base of a software project is usually contained in multiple C or C++ source files, object files, libraries, and header files. Как правило, файл makefile координирует объединение этих элементов в исполняемый файл.Typically, a makefile coordinates the combination of these elements into an executable file. На следующем рисунке показана структура файла makefile, использующего файл предкомпилированного заголовка.The following figure shows the structure of a makefile that uses a precompiled header file. Имена макросов и имена файлов на рисунке соответствуют приведенным в примере кода в разделах и .The NMAKE macro names and the file names in this diagram are consistent with those in the example code found in and .

На рисунке для отображения последовательности процесса сборки используются три схематических элемента.The figure uses three diagrammatic devices to show the flow of the build process. Прямоугольники представляют каждый файл или макрос; три макроса представляют один или несколько файлов.Named rectangles represent each file or macro; the three macros represent one or more files. Затененные области представляют каждое действие компиляции или компоновки.Shaded areas represent each compile or link action. Стрелки показывают, какие файлы и макросы объединяются во время процесса компиляции или компоновки.Arrows show which files and macros are combined during the compilation or linking process.

Структура файла makefile, использующего файл предкомпилированного заголовкаStructure of a Makefile That Uses a Precompiled Header File

Начиная с верхней части диаграммы, STABLEHDRS и BOUNDRY являются макросами NMAKE, в которых вы перечислите файлы, которые, скорее всего, не потребуют перекомпиляции.Beginning at the top of the diagram, both STABLEHDRS and BOUNDRY are NMAKE macros in which you list files not likely to need recompilation. Эти файлы компилируются с помощью командной строкиThese files are compiled by the command string

только если файл предкомпилированного заголовка (STABLE.pch) не существует или если вы вносите изменения в файлы, перечисленные в двух макросах.only if the precompiled header file (STABLE.pch) does not exist or if you make changes to the files listed in the two macros. В любом случае файл предкомпилированного заголовка будет содержать код только из файлов, перечисленных в макросе STABLEHDRS.In either case, the precompiled header file will contain code only from the files listed in the STABLEHDRS macro. Перечислите последний файл, который необходимо предварительно откомпилировать в макросе BOUNDRY.List the last file you want precompiled in the BOUNDRY macro.

Файлы, перечисленные в этих макросах, могут быть файлами заголовков или файлами исходного кода C или C++.The files you list in these macros can be either header files or C or C++ source files

(Один файл PCH нельзя использовать одновременно с модулями C и C++.) Обратите внимание, что можно использовать макрос hdrstop, чтобы прервать предварительную компиляцию в любой момент в файле BOUNDRY.(A single PCH file cannot be used with both C and C++ modules.) Note that you can use the hdrstop macro to stop precompilation at some point within the BOUNDRY file. Дополнительные сведения см

в разделе hdrstop.See hdrstop for more information.

Далее APPLIB.obj на схеме представляет код поддержки, используемый в окончательном приложении.Continuing down the diagram, APPLIB.obj represents the support code used in your final application. Он создается из APPLIB.cpp, файлов, перечисленных в макросе UNSTABLEHDRS, и предварительно скомпилированного кода из предкомпилированного заголовка.It is created from APPLIB.cpp, the files listed in the UNSTABLEHDRS macro, and precompiled code from the precompiled header.

MYAPP.obj представляет конечное приложение.MYAPP.obj represents your final application. Он создается из файла MYAPP.cpp, файлов, перечисленных в макросе UNSTABLEHDRS, и предварительно скомпилированного кода из предкомпилированного заголовка.It is created from MYAPP.cpp, the files listed in the UNSTABLEHDRS macro, and precompiled code from the precompiled header.

Наконец, создается исполняемый файл (MYAPP.EXE) путем связывания файлов, перечисленных в макросе OBJS (APPLIB.obj и MYAPP.obj).Finally, the executable file (MYAPP.EXE) is created by linking the files listed in the OBJS macro (APPLIB.obj and MYAPP.obj).

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector