告诉编译器只包含一次文件有什么意义?默认情况下是否有意义?甚至没有理由多次包含一个文件吗?为什么不只是假设呢?与特定的硬件有关吗?
告诉编译器只包含一次文件有什么意义?默认情况下是否有意义?甚至没有理由多次包含一个文件吗?为什么不只是假设呢?与特定的硬件有关吗?
<assert.h>
多次包含不同的定义NDEBUG
。
#pragma once
其本身而言,在某些硬件环境(通常是具有网络驱动器,并且可能有多个路径指向同一标头)的地方,它们将无法正常工作。
#pragma once
假定,应对该默认设置的方式是什么? #pragma many
?有多少编译器实现了这样的功能?
Answers:
这里有多个相关问题:
为什么#pragma once
不自动执行?
因为在某些情况下您想多次包含文件。
为什么要多次包含文件?
其他答案(Boost.Preprocessor,X-Macros,包括数据文件)中给出了几个原因。我想添加一个“避免代码重复”的特定示例:OpenFOAM鼓励一种#include
在函数中包含点点滴滴是一种常见概念的样式。例如,请参阅此讨论。
好的,但是为什么不选择退出默认设置?
因为它不是标准实际指定的。#pragma
根据定义,s是特定于实现的扩展。
为什么还#pragma once
没有成为标准化功能(得到广泛支持)?
因为以与平台无关的方式固定“相同文件”实际上是非常困难的。有关更多信息,请参见此答案。
#pragma STDC
家庭。但是它们都可以控制实现定义的行为。
#ifndef XX
,则在#endif
读取整个文件之前,它必须不知道在对应的内容之后是否还有任何内容。该保持的最外层是否跟踪编译器#ifndef
包含整个文件和笔记什么的宏它检查可能能够避免再次扫描文件,而是一个指令,说没有什么遵循目前的点似乎很重要的似乎更好比在编译器依赖于记住这样的事情。
您可以在文件中的#include
任何地方使用,而不仅仅是在全局范围内使用,例如在函数内部使用(如果需要,可以多次使用)。当然,丑陋而不是好的样式,但是可能并且有时是明智的(取决于所包含的文件)。如果#include
只是一次性的事情,那将是行不通的。#include
毕竟只是做愚蠢的文本替换(剪切'n'粘贴),并不是您包括的所有内容都必须是头文件。例如,您可能会#include
包含一个包含自动生成的数据的文件,该文件包含用于初始化的原始数据std::vector
。喜欢
std::vector<int> data = {
#include "my_generated_data.txt"
}
并让“ my_generation_data.txt”成为编译过程中由构建系统生成的东西。
或者,也许我懒惰/愚蠢/愚蠢,并将其放在文件中(非常人为的示例):
const noexcept;
然后我做
class foo {
void f1()
#include "stupid.file"
int f2(int)
#include "stupid.file"
};
另一个人为设计较少的示例是源文件,其中许多函数需要在命名空间中使用大量类型,但是您不想只说using namespace foo;
全局,因为这会污染很多其他东西你不要 因此,您创建了一个包含以下内容的文件“ foo”
using std::vector;
using std::array;
using std::rotate;
... You get the idea ...
然后在源文件中执行此操作
void f1() {
#include "foo" // needs "stuff"
}
void f2() {
// Doesn't need "stuff"
}
void f3() {
#include "foo" // also needs "stuff"
}
注意:我不主张这样做。但是在某些代码库中这是可能的并且已经完成了,我不明白为什么不应该这样做。它确实有其用途。
也可能是您包含的文件的行为取决于某些宏(#define
s)的值。因此,您可能需要在更改一些值之后将文件包含在多个位置,以便在源文件的不同部分获得不同的行为。
#define
在每个include更改s的值之前更改了s的值,那么s的值会更改包含文件的行为,那么我可能非常需要多次包含它,才能在源文件的不同部分获得不同的行为。
可以多次使用,例如,使用X宏技术:
data.inc:
X(ONE)
X(TWO)
X(THREE)
use_data_inc_twice.c
enum data_e {
#define X(V) V,
#include "data.inc"
#undef X
};
char const* data_e__strings[]={
#define X(V) [V]=#V,
#include "data.inc"
#undef X
};
我对其他用途一无所知。
#pragma once
行为将是一项重大更改。
您似乎在假设即使语言中也存在“ #include”功能的目的是为将程序分解为多个编译单元提供支持。那是不对的。
它可以执行该角色,但这不是其预期目的。C最初被开发为比PDP-11 Macro-11汇编程序稍微高级的语言,用于重新实现Unix。它具有宏预处理器,因为这是Macro-11的功能。它具有从文本上包含另一个文件中的宏的功能,因为这是Macro-11的功能,Macro-11的功能是将它们移植到新的C编译器中并已使用。
现在事实证明,“#include”可用于将代码分离为编译单元,这(有点可笑)很有用。然而,事实证明这个hack存在意味着它成为了在C.做到这一点的方式存在意味着没有新的方法不断的事实之路需要要创建提供此功能,所以没有什么更安全(如:不容易受到多-inclusion)。由于它已经在C语言中,因此它与C语言的其余大部分语法和惯用法一起被复制到C ++中。
有建议为C ++提供适当的模块系统,以便最终可以消除这个已有45年历史的预处理程序。我不知道这有多迫切。十多年来,我一直在听说它的存在。
不,这会严重阻碍图书馆作家等可用的选项。例如,Boost.Preprocessor允许一个人使用预处理器循环,而实现这些循环的唯一方法是对同一文件进行多次包含。
Boost.Preprocessor是许多非常有用的库的构建块。
#pragma reentrant
或类似的东西是完全明智的。后见之明是20/20。
once
还是reentrant
,以减轻此问题或其他潜在问题。
在我主要研究的产品的固件中,我们需要能够指定应在内存中分配函数和全局/静态变量的位置。实时处理需要驻留在芯片上的L1内存中,以便处理器可以直接,快速地访问它。不太重要的处理可以放在芯片的L2存储器中。不需要特别迅速处理的任何内容都可以存在于外部DDR中并进行缓存,因为速度稍慢一点都没有关系。
#pragma分配要去的地方很长,很重要。容易弄错。犯错的效果将是代码/数据将被悄悄放进默认(DDR)内存,并且效果是可能的闭环控制停止无缘无故,很容易看到工作。
因此,我使用包含文件,其中仅包含该编译指示。我的代码现在看起来像这样。
头文件...
#ifndef HEADERFILE_H
#define HEADERFILE_H
#include "set_fast_storage.h"
/* Declare variables */
#include "set_slow_storage.h"
/* Declare functions for initialisation on startup */
#include "set_fast_storage.h"
/* Declare functions for real-time processing */
#include "set_storage_default.h"
#endif
来源...
#include "headerfile.h"
#include "set_fast_storage.h"
/* Define variables */
#include "set_slow_storage.h"
/* Define functions for initialisation on startup */
#include "set_fast_storage.h"
/* Define functions for real-time processing */
您会在此处注意到同一文件的多个包含,即使只是在标头中也是如此。如果我现在输入错误,编译器会告诉我找不到包含文件“ set_fat_storage.h”,我可以轻松地对其进行修复。
因此,在回答您的问题时,这是一个实际的,实际的示例,其中要求多个包含。
_Pragma
指令的激励示例。现在可以从常规宏扩展相同的编译指示。因此,无需多次包含。
#ifdefs
。所以,你可能会说#define MODE_ONE 1
,然后#include "has-modes.h"
,然后#undef MODE_ONE
用#define MODE_TWO 1
和#include "has-modes.h"
一次。预处理器对这类事情是不可知的,也许有时它们是有意义的。