为什么不自动假设#pragma?

问题描述 投票:74回答:6

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

c++ c pragma
6个回答
80
投票

这里有多个相关问题:

  • 为什么#pragma once不会自动执行? 因为在某些情况下您希望多次包含文件。
  • 为什么要多次包含文件? 其他答案(Boost.Preprocessor,X-Macros,包括数据文件)中给出了几个原因。我想添加一个“避免代码重复”的特定示例:OpenFOAM鼓励一种风格,其中功能中的#includeing位是一个常见的概念。例如,参见this讨论。
  • 好的,但是为什么它不是选择退出的默认值? 因为它实际上并未由标准指定。根据定义,#pragmas是特定于实现的扩展。
  • 为什么#pragma once还没有成为标准化功能(因为它得到广泛支持)? 因为以平台无关的方式固定“同一个文件”实际上是非常困难的。 See this answer for more information

36
投票

您可以在文件中的任何位置使用#include,而不仅仅是在全局范围内 - 例如,在函数内部(如果需要,可以多次使用)。当然,丑陋和不好的风格,但可能和偶尔明智(取决于你包括的文件)。如果#include只是一次性的事情那么那就行不通了。 #include毕竟只是做了愚蠢的文本替换(cut'n'paste),并不是你所包含的所有内容都必须是头文件。你可以 - 例如 - #include一个包含自动生成数据的文件,其中包含初始化std::vector的原始数据。喜欢

std::vector<int> data = {
#include "my_generated_data.txt"
}

并且“my_generated_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"
}

注意:我不是在提倡做这样的事情。但是有可能在一些代码库中完成,我不明白为什么不应该允许它。它确实有它的用途。

也可能是您包含的文件的行为有所不同,具体取决于某些宏(#defines)的值。因此,您可能希望在首次更改某个值后将文件包含在多个位置,以便在源文件的不同部分中获得不同的行为。


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
};

我不知道任何其他用途。


19
投票

您似乎在假设即使语言中存在的“#include”功能的目的是为了将程序分解为多个编译单元提供支持的情况下运行。那是不对的。

它可以执行该角色,但这不是其预期目的。 C是originally developed,是比重新实现Unix的PDP-11 Macro-11 Assembly更高级的语言。它有一个宏预处理器,因为这是Macro-11的一个功能。它能够以文本方式包含来自另一个文件的宏,因为这是Macro-11的一个特性,它们移植到新C编译器的现有Unix已经使用了。

现在事实证明“#include”对于将代码分成编译单元很有用,因为(可以说)有点像黑客。然而,这种黑客存在的事实意味着它成为了在C中完成的方式。存在一种方式的事实意味着不需要创建任何新方法来提供这种功能,因此没有更安全(例如:不易受多重影响) -inclusion)曾经创造过。因为它已经在C中,所以它被复制到C ++以及C语言和惯用语的其余部分。

有一个提供给C ++ a proper module system的建议,所以这个45岁的预处理器黑客最终可以免除。我不知道这是多么迫近。我已经听说它已经有十多年了。


10
投票

不,这将显着阻碍例如图书馆作者可用的选项。例如,Boost.Preprocessor允许使用预处理器循环,实现这些循环的唯一方法是通过同一文件的多个包含。

Boost.Preprocessor是许多非常有用的库的构建块。


8
投票

在我主要使用的产品的固件中,我们需要能够指定应在内存中分配函数和全局/静态变量的位置。实时处理需要存储在芯片上的L1存储器中,以便处理器可以直接,快速地访问它。不太重要的处理可以在芯片上的L2存储器中进行。任何不需要特别及时处理的东西都可以存在于外部DDR中并通过缓存,因为如果速度稍慢则没关系。

分配到哪里的#pragma是一条很长的,非平凡的线。这很容易弄错。错误的结果是代码/数据将被默默地置于默认(DDR)内存中,其效果可能是闭环控制停止工作,无缘无故容易看到。

所以我使用include文件,其中只包含该pragma。我的代码现在看起来像这样。

头文件......

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

因此,在回答您的问题时,这是一个真实,实用的例子,说明需要多个包含。

© www.soinside.com 2019 - 2024. All rights reserved.