为什么不自动假定#pragma?

时间:2018-11-21 16:35:34

标签: c++ c pragma

告诉编译器只包含一次文件有什么意义?默认情况下是否有意义?甚至没有理由多次包含一个文件吗?为什么不只是假设呢?与特定的硬件有关吗?

6 个答案:

答案 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”,我可以轻松修复它。

因此,在回答您的问题时,这是一个实际的,实际的示例,其中要求多个包含。