告诉编译器只包含一次文件有什么意义?默认情况下是否有意义?甚至没有理由多次包含一个文件吗?为什么不只是假设呢?与特定的硬件有关吗?
答案 0 :(得分:80)
这里有多个相关问题:
为什么#pragma once
没有自动执行?
因为在某些情况下您想多次包含文件。
为什么要多次包含文件?
其他答案(Boost.Preprocessor,X-Macros,包括数据文件)中给出了几个原因。我想添加一个“避免代码重复”的特定示例: OpenFOAM 鼓励一种样式,其中#include
在函数内的点点滴滴是一个普遍的概念。例如,请参见this讨论。
好,但是为什么不选择退出默认设置?
因为它不是标准实际指定的。 #pragma
是定义上特定于实现的扩展。
为什么#pragma once
尚未成为标准化功能(得到广泛支持)?
因为以与平台无关的方式固定“相同文件”实际上是非常困难的。 See this answer for more information。
答案 1 :(得分:36)
您可以在文件中的任意位置使用#include
,而不仅仅是在全局范围内使用-例如,在函数内部(如果需要,可以多次使用)。当然,丑陋而不是好的样式,但是可能并且有时是明智的(取决于所包含的文件)。如果#include
只是一次性的东西,那将是行不通的。毕竟,#include
只是做愚蠢的文本替换(剪切'n'paste),并不是您包括的所有内容都必须是头文件。您可能-例如-#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)的值,所包含的文件的行为也可能不同。因此,您可能需要在更改一些值之后将文件包含在多个位置,以便在源文件的不同部分获得不同的行为。
答案 2 :(得分:26)
例如,可以使用X-macro技术来包含多次:
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
};
我对其他用途一无所知。
答案 3 :(得分:19)
您似乎在假设即使语言中也存在“ #include”功能的目的是为将程序分解为多个编译单元提供支持。 那是不正确的。
它可以执行该角色,但这不是其预期目的。与重新实现Unix的PDP-11 originally developed汇编语言相比,C Macro-11的语言略高一些。它具有宏预处理器,因为这是Macro-11的功能。它能够以文本方式包含另一个文件中的宏,因为这是Macro-11的一项功能,Macro-11正在将其移植到其新C编译器中使用该Unix。
现在,事实证明,“#include”对于将代码分离为编译单元是很有用的(可能是个hack)。但是,该hack存在的事实意味着它成为了用C语言完成的方式。存在一种方法的事实意味着不需要创建任何新方法来提供此功能,因此没有什么更安全的了(例如:不易遭受多重包容)。由于它已经在C语言中,因此它与C语言的其余大部分语法和惯用法一起被复制到C ++中。
有一个建议提供C ++ a proper module system,以便最终可以省掉这个已有45年历史的预处理程序。我不知道这有多迫切。十多年来,我一直在听说它的存在。
答案 4 :(得分:10)
不,这会严重阻碍例如库编写者可用的选项。例如,Boost.Preprocessor允许一个人使用预处理器循环,而实现这些循环的唯一方法是对同一文件进行多次包含。
Boost.Preprocessor是许多非常有用的库的构建块。
答案 5 :(得分:8)
在我主要研究的产品的固件中,我们需要能够指定应在内存中分配函数和全局/静态变量的位置。实时处理需要驻留在芯片上的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”,我可以轻松修复它。
因此,在回答您的问题时,这是一个实际的,实际的示例,其中要求多个包含。