你有没有写过没有警卫的标题?

时间:2021-04-29 15:04:17

I am wondering why C++ compilers don't generate header guards automatically for headers?

我想知道为什么c++编译器不会自动生成header的标题?

// Why do I have to write this for every .hpp file I create?!!
#ifndef myheader_hpp__
#define myheader_hpp__
// ...
#endif

I haven't met a situation where they aren't needed when I write my headers. I can't see a real use-case of the opposite behavior, but I would be glad to see one. Is there a technical difficulty or is it just history?!

我还没有遇到过在我写标题时不需要它们的情况。我看不到相反行为的真正用例,但我很高兴看到一个。这是技术上的困难还是历史?!

4 个解决方案

#1


10  

There are some preprocessor tricks that require the same header included multiple times into the same compilation unit. Another reference.

有一些预处理技巧要求同一头包含多次到同一编译单元中。另一个参考。

Besides that, most compilers do allow you to shorten all of that down to:

除此之外,大多数编译器允许您将所有这些缩短为:

#pragma once

#2


2  

To a compiler, automatically putting in include guards isn't truly practical. You can define prototypes for methods/functions/classes/etc without running into problems, and this is usually done in a header. When a class is defined in a header, though, you run into the problem of having the class defined more than once by the compiler if it is included by two different .cpp files or other headers.

对于编译器来说,自动加入include保护并不是真正可行的。您可以在不遇到问题的情况下定义方法/函数/类的原型,这通常是在header中完成的。但是,当一个类在头文件中定义时,如果有两个不同的.cpp文件或其他头文件包含这个类,那么编译器将不止一次地定义这个类。

Really, include guards are just one trick for headers. You don't always need them, and there are some cases where you wouldn't use them. This is actually easier, believe it or not.

确实,包括后卫只是头球的一个技巧。你并不总是需要它们,有些情况下你不会用到它们。信不信由你,这其实更简单。

#3


2  

Because it's a general purpose mechanism to insert one file into another.

因为将一个文件插入另一个文件是一种通用的机制。

It's just that general purpose ability is used for a very common specific purpose in 99% of the cases.

只是一般目的能力在99%的情况下被用于一个非常常见的特定目的。

#4


2  

I'll simply point you to the Clang / LLVM project.

我将简单地向您介绍Clang / LLVM项目。

In this project, they created a way to encode data using a simple descriptive language, that is then fed up to a tool (called tblgen for Table Generator) that is meant to produce a C++ file. For example, the diagnostics:

在这个项目中,他们创建了一种使用简单的描述性语言对数据进行编码的方法,然后对一个工具(表生成器的tblgen)感到厌烦,该工具旨在生成一个c++文件。例如,诊断:

let Component = "Sema" in {
let CategoryName = "Semantic Issue" in {

// Constant expressions
def err_expr_not_ice : Error<
  "expression is not an integer constant expression">;

....

There are a few thousands diagnostics in Clang, separated in several files. Once processed by tblgen, they will generate a huge .inc file which, for each diagnostic, will contain a macro call. By defining the macro and including the file, you can produce a C++ table (or anything else really, but the use is generally for tables):

在Clang中有数千个诊断信息,它们在几个文件中分开。一旦由tblgen处理,它们将生成一个巨大的.inc文件,对于每个诊断,将包含一个宏调用。通过定义宏并包含文件,您可以生成一个c++表(或其他任何东西,但通常用于表):

static const StaticDiagInfoRec StaticDiagInfo[] = {
#define DIAG(ENUM,CLASS,DEFAULT_MAPPING,DESC,GROUP,               \
             SFINAE,ACCESS,NOWERROR,SHOWINSYSHEADER,              \
             CATEGORY,BRIEF,FULL)                                 \
  { diag::ENUM, DEFAULT_MAPPING, CLASS, SFINAE, ACCESS,           \
    NOWERROR, SHOWINSYSHEADER, CATEGORY,                          \
    STR_SIZE(#ENUM, uint8_t), STR_SIZE(GROUP, uint8_t),           \
    STR_SIZE(DESC, uint16_t), STR_SIZE(BRIEF, uint16_t),          \
    STR_SIZE(FULL, uint16_t),                                     \
    #ENUM, GROUP, DESC, BRIEF, FULL },
#include "clang/Basic/DiagnosticCommonKinds.inc"
#include "clang/Basic/DiagnosticDriverKinds.inc"
#include "clang/Basic/DiagnosticFrontendKinds.inc"
#include "clang/Basic/DiagnosticLexKinds.inc"
#include "clang/Basic/DiagnosticParseKinds.inc"
#include "clang/Basic/DiagnosticASTKinds.inc"
#include "clang/Basic/DiagnosticSemaKinds.inc"
#include "clang/Basic/DiagnosticAnalysisKinds.inc"
#undef DIAG
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};

And the same files can produce different tables, since you are free to write the macros as you wish.

同样的文件可以生成不同的表,因为您可以随意编写宏。

It is, of course, a very specific use.

当然,这是一个非常具体的用途。

But don't worry, even though the modules didn't make it into C++11, we can hope for C++1x.

但是不要担心,即使这些模块没有进入c++ 11,我们还是可以期待c++ 1x。

#1


10  

There are some preprocessor tricks that require the same header included multiple times into the same compilation unit. Another reference.

有一些预处理技巧要求同一头包含多次到同一编译单元中。另一个参考。

Besides that, most compilers do allow you to shorten all of that down to:

除此之外,大多数编译器允许您将所有这些缩短为:

#pragma once

#2


2  

To a compiler, automatically putting in include guards isn't truly practical. You can define prototypes for methods/functions/classes/etc without running into problems, and this is usually done in a header. When a class is defined in a header, though, you run into the problem of having the class defined more than once by the compiler if it is included by two different .cpp files or other headers.

对于编译器来说,自动加入include保护并不是真正可行的。您可以在不遇到问题的情况下定义方法/函数/类的原型,这通常是在header中完成的。但是,当一个类在头文件中定义时,如果有两个不同的.cpp文件或其他头文件包含这个类,那么编译器将不止一次地定义这个类。

Really, include guards are just one trick for headers. You don't always need them, and there are some cases where you wouldn't use them. This is actually easier, believe it or not.

确实,包括后卫只是头球的一个技巧。你并不总是需要它们,有些情况下你不会用到它们。信不信由你,这其实更简单。

#3


2  

Because it's a general purpose mechanism to insert one file into another.

因为将一个文件插入另一个文件是一种通用的机制。

It's just that general purpose ability is used for a very common specific purpose in 99% of the cases.

只是一般目的能力在99%的情况下被用于一个非常常见的特定目的。

#4


2  

I'll simply point you to the Clang / LLVM project.

我将简单地向您介绍Clang / LLVM项目。

In this project, they created a way to encode data using a simple descriptive language, that is then fed up to a tool (called tblgen for Table Generator) that is meant to produce a C++ file. For example, the diagnostics:

在这个项目中,他们创建了一种使用简单的描述性语言对数据进行编码的方法,然后对一个工具(表生成器的tblgen)感到厌烦,该工具旨在生成一个c++文件。例如,诊断:

let Component = "Sema" in {
let CategoryName = "Semantic Issue" in {

// Constant expressions
def err_expr_not_ice : Error<
  "expression is not an integer constant expression">;

....

There are a few thousands diagnostics in Clang, separated in several files. Once processed by tblgen, they will generate a huge .inc file which, for each diagnostic, will contain a macro call. By defining the macro and including the file, you can produce a C++ table (or anything else really, but the use is generally for tables):

在Clang中有数千个诊断信息,它们在几个文件中分开。一旦由tblgen处理,它们将生成一个巨大的.inc文件,对于每个诊断,将包含一个宏调用。通过定义宏并包含文件,您可以生成一个c++表(或其他任何东西,但通常用于表):

static const StaticDiagInfoRec StaticDiagInfo[] = {
#define DIAG(ENUM,CLASS,DEFAULT_MAPPING,DESC,GROUP,               \
             SFINAE,ACCESS,NOWERROR,SHOWINSYSHEADER,              \
             CATEGORY,BRIEF,FULL)                                 \
  { diag::ENUM, DEFAULT_MAPPING, CLASS, SFINAE, ACCESS,           \
    NOWERROR, SHOWINSYSHEADER, CATEGORY,                          \
    STR_SIZE(#ENUM, uint8_t), STR_SIZE(GROUP, uint8_t),           \
    STR_SIZE(DESC, uint16_t), STR_SIZE(BRIEF, uint16_t),          \
    STR_SIZE(FULL, uint16_t),                                     \
    #ENUM, GROUP, DESC, BRIEF, FULL },
#include "clang/Basic/DiagnosticCommonKinds.inc"
#include "clang/Basic/DiagnosticDriverKinds.inc"
#include "clang/Basic/DiagnosticFrontendKinds.inc"
#include "clang/Basic/DiagnosticLexKinds.inc"
#include "clang/Basic/DiagnosticParseKinds.inc"
#include "clang/Basic/DiagnosticASTKinds.inc"
#include "clang/Basic/DiagnosticSemaKinds.inc"
#include "clang/Basic/DiagnosticAnalysisKinds.inc"
#undef DIAG
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};

And the same files can produce different tables, since you are free to write the macros as you wish.

同样的文件可以生成不同的表,因为您可以随意编写宏。

It is, of course, a very specific use.

当然,这是一个非常具体的用途。

But don't worry, even though the modules didn't make it into C++11, we can hope for C++1x.

但是不要担心,即使这些模块没有进入c++ 11,我们还是可以期待c++ 1x。