如果我把一个标题(啊)放到stdafx.h中并且该标题包含stdafx.h中没有提到的另一个标题(bh),那么每次有人包含啊或者它是作为一部分编译的时候会被访问啊?如果编译成啊,有人直接包含bh会发生什么?这会被预编译吗?
我提出这个问题的动机是我正在尝试优化我工作的软件的stdafx.h文件的内容.重建和增量构建时间对我们都很重要.我想知道我是否可以简单地搜索#include指令的所有.cpp文件并计算每个文件的包含次数.经常包含的文件可能是stdafx.h文件的良好候选者.当然,如果我不仅要考虑包含哪些文件,还要考虑所包含文件包含哪些文件,这种策略完全是假的.
我怀疑这很重要,但我们正在使用Visual Studio 2005.
该问题涉及Visual Studio编译器创建的.pch二进制文件的内容.它包含什么?它只是头文件的解析树,还是对象代码?
考虑这个例子:
// myheader.h
#include <vector>
class A {
public:
void add(int i) { v.push_back(i); }
private:
std::vector<int> v;
};
Run Code Online (Sandbox Code Playgroud)
在预编译的集合中包含此标头会导致vector <int>的完整模板实例化被编译并添加到.pch中吗?
提供更多背景; 如果只预编译了解析树,这意味着每个编译单元仍然会创建一个实例化模板的目标代码,导致编译和链接时间增加.因此,即使启用了预编译头,"统一构建"/减少编译单元仍然是减少构建时间的相关因素.
我已经使用预编译头一段时间了,并被告知(并看到)它们如何减少编译时间。但我真的很想知道发生了什么(在引擎盖下),以便它可以使我的编译更快。
因为据我所知,在 .cpp 中添加未使用的包含会减慢您的编译时间,并且头文件可能包含大量未使用的头文件到 .cpp。
那么预编译头文件如何使我的编译速度更快呢?
总结:今天我发现当没有预编译头文件构建DLL时,当你尝试使用它时会出现一个奇怪的错误.
禁用预编译头时,构建DLL会很好.但是,只要附加DLL(编译时或运行时),就会导致错误"无效参数".两种情况下的实际错误代码都不同.当附加编译时弹出一个错误代码为0xc000000d的对话框,当调用LoadLibrary()它时返回一个NULL指针并GetLastError()返回0x57.
编辑:
我发现当禁用增量链接时问题就会消失.不知何故,我错过了Visual Studio在运行附加到DLL编译时的客户端时显示的以下错误:
'TestClient.exe': Loaded 'D:\Projects\PchDllTest2\Debug\TestClient.exe', Symbols loaded.
'TestClient.exe': Loaded 'C:\WINDOWS\system32\ntdll.dll', Cannot find or open the PDB file
'TestClient.exe': Loaded 'C:\WINDOWS\system32\kernel32.dll', Cannot find or open the PDB file
'TestClient.exe': Loaded 'D:\Projects\PchDllTest2\Debug\TestDll.dll', Symbols loaded.
SXS: RtlCreateActivationContext() failed 0xc000000d
LDR: LdrpWalkImportDescriptor() failed to probe D:\Projects\PchDllTest2\Debug\TestDll.dll for its manifest, ntstatus 0xc000000d
Debugger:: An unhandled non-continuable exception was thrown during process load
The program '[5292] TestClient.exe: Native' has exited with code -1073741811 (0xc000000d).
Run Code Online (Sandbox Code Playgroud)
根据要求,函数声明:
#ifdef __cplusplus …Run Code Online (Sandbox Code Playgroud) 在阅读了这篇精彩的文章(预编译标题的关注和喂养)之后,我对这些文章在现实生活中如何实际起作用存在疑问.更具体地说,我怎么知道在以下场景中我需要触发预编译头的重建:
#define在我的一个.cpp文件中改变预处理器解释已经包含在我的预编译头中的一些头的方式#define是特定的预处理器指令,它改变了预处理器解释已经包含在预编译头中的标头的方式#include其他标头时,前一个问题可以递归发生是否应该使用预编译的头文件强制执行某种限制性编码样式,例如将.cpp文件中包含的头文件数限制为一个并且永远不会将#define内容限制在.cpp文件中?
虽然微软的编译器可能在预先编译的头文件中做了不错的工作(通过应用一些特定于MS的voodoo),因为据我所知,它提供了应该做所有管道的选项/Yc和/Yu选项,对于GCC来说,似乎这个功能需要Makefile中的大量手动工作和创造力,我无法找到应该解决使用预编译头文件的所有陷阱的模板.
例如,如果我有一个构建多个库的项目,为了在每次更改后不重建所有库,我必须在Makefile中使用一些非常可爱的sed技巧来检测#include当前库中的一个头文件是否被修改(或者它#include是一个修改过的标题).我担心甚至会想到预先构建的头文件实际上意味着的复杂性,以便构建脚本在每次必要时重建它们.
我已经搜遍了一些关于#pragma曾经实际做过什么的澄清,并且找不到我仍然存在的一些问题的确切答案.
#pragma是否曾确保它所包含的头文件只被调用一次AS是否包含在所述头文件中的头文件尚未包括在内?此外,如果只调用一次,这是否意味着需要特定标头的.cpp文件将无法访问它?如果头文件标记为#pragma一次并包含在.cpp中,那么该头文件是否可以在其他地方再次使用?
这些是我没有找到的各种澄清.对不起,如果有文件在某处澄清这一点,但我真的找不到任何具体的东西.
我遇到了有关预编译标头和#include指令的用法的理解问题。所以我在这里得到了“ stdafx.h”,并在其中包含了例如vector,iostream和string。显然,关联的“ stdafx.cpp”仅包含“ stdafx.h”。
因此,如果我设计自己的头文件使用例如vector或iostream中的“代码”,则必须包含头文件,因为编译器当时不知道声明。那么,为什么这里的某些帖子(在头文件或源文件中包含stdafx.h?)说,即使该文件包含了所需的例如vector的声明,在其他头文件中包含“ stdafx.h”也是不好的吗?因此,基本上没有关系直接包含向量或预编译的头文件都一样。
我当然知道,如果关联的源文件包含所需的头文件,则不必在另一个头文件中包含头文件,因为那时声明是已知的。好吧,这仅在头文件包含在某处的情况下才有效。
所以我的问题是:我应该避免在任何源文件中包含预编译的头文件,为什么?我有点困惑,因为我在网上阅读矛盾的表达,我无论如何都不应该在头文件中包含任何内容,或者可以在头文件中包含它吗?那现在怎么了?
我不时(并非每次)都收到此错误消息,我在混合模式项目中进行了编译(编辑:对不起,我在这里并没有明确指出:我的意思是“重建”)。而且Visual Studio告诉我“使用'-Zm114'或更高版本的命令行选项重新编译”。原则上没问题,我只是按照VS告诉我的那样做。
但是目前,这有两个问题:
为什么在每次重建时都不会发生?如果我理解正确,则编译器在编译项目时会耗尽内存。因此,如果我进行重建以清理所有先前的工作,如果我什么都没做,它下次是否也不会耗尽内存?
为了安全起见,我已经Zm120在该项目的所有配置中为Zm(即)指定了120的值。为什么我收到一个错误消息,该值较低?还是114的建议值只是对VS的疯狂猜测?
c++ mixed-mode compiler-errors precompiled-headers visual-studio-2015
在我的代码库中,我有一个include文件(MyBaseDefinitions.h)执行a pragma diagnostic push后跟禁用很多警告.如果要重新启用这些警告,则只需在其后添加另一个标头,即pragma诊断pop(EndMyBaseDefinitions.h).
这似乎工作得很好,除了在Clang放入MyBaseDefinitions.h预编译的头文件时.看来诊断"堆栈"在预编译头中丢失了.所以,让我说我有MyPrecompiledHeader.h,它有:
#include "MyBaseDefinitions.h" // does the pragma diagnostic push
然后在我的演示文件中我做:
#include "MyPrecompiledHeader.h"
#include "HeaderExample1.h" // This file has some warnings in it that we don't care about
#include "EndMyBaseDefinitions.h" // Re-enable warnings
// ...
Run Code Online (Sandbox Code Playgroud)
我会收到警告:
error : pragma diagnostic pop could not pop, no matching push [-Werror,-Wunknown-pragmas]
这是一个已知的问题?显然,理想情况下我根本不需要做任何警告禁用,但如果可能的话,让我们暂时忽略这个细节.
--Joel
示例:说我在预编译的头文件中包括:
#include <vector>
Run Code Online (Sandbox Code Playgroud)
由于在我的项目中经常使用vector的一些实例,例如std :: vector,std :: vector等,因此,如果我在预编译标头中也将它们实例化,它将减少编译时间:
#include <vector>
template class std::vector<float>;
template class std::vector<int>;
Run Code Online (Sandbox Code Playgroud)
更进一步,将伪函数添加到使用一些函数的预编译头中是否有意义:
namespace pch_detail {
inline auto func() {
auto&& v = std::vector<float>{};
v.size();
v.begin();
v.front();
}
}
Run Code Online (Sandbox Code Playgroud)
我不确定翻译单元和模板如何真正工作,因此在我看来,如果我在预编译的标头中实例化它们,这意味着不必为每个.cpp文件都实例化它们。
更新资料
使用Visual Studio 2017和一些常用模板类的实例化在真实世界的代码库上进行了测试。
因此,至少就我而言,这花费了更多时间。
c++ ×10
c ×1
clang ×1
compilation ×1
compile-time ×1
dependencies ×1
dll ×1
gcc ×1
include ×1
mixed-mode ×1
pragma ×1
stdafx.h ×1
templates ×1