《The _Pragma("once") vs include guards》

我寫的issue原文:

標題:Consider changing include guards to pragma once.

We can use the _Pragma directive for header file guard.

example:

#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_

change to

_Pragma("once")

There are several factors to support the lowcase file/directory name.

portability
Cross-platform problems will not occur, compiler implement standard, they will support it.
readability
Generally, if you just want to do some guard, you should not care about the name and the directory of this file. and the naming of the guard is yet another big question, nonstandard things always end up in a fight.
convenience
just copy & paste for the _Pragma("once"), here and there.

Computer industry set the standards, so does our company.

feel free to refer to
tencent cplusplus code standard, “2.2.【必須】 頭文件保護”:
https://git.code.oa.com/standards/cpp#22%E5%BF%85%E9%A1%BB-%E5%A4%B4%E6%96%87%E4%BB%B6%E4%BF%9D%E6%8A%A4

image.png

More importantly, the _Pragma directive is the standard level in C++, instead of the a specific compiler extension like GCC's # pragma, which maximizes stability and compatibility.

see:


我寫的wiki原文(工程標準 -> C++ Coding Guideline):

標題:The _Pragma("once") Guard

_Pragma操作符

在C/C++標準中,#pragma是一條預處理的指令(preprocessor directive)。簡單地說,#pragma是用來向編譯器傳達語言標準以外的一些信息。舉個簡單的例子,如果我們在代碼的頭文件中定義了以下語句:

#pragma once

要達到與上例#pragma類似的效果,則只需要如下代碼即可。

_Pragma("once");

而相比預處理指令#pragma,由于_Pragma是一個操作符,因此可以嵌套在宏中。我們可以看看下面這個例子:

#define CONCAT(x) PRAGMA(concat on #x)
#define PRAGMA(x) _Pragma(#x)
CONCAT( ..\concat.dir )

這里,CONCAT( ..concat.dir )最終會產生_Pragma(concat on "..concat.dir")這樣的效果。而#pragma則不能在宏中展開,因此從靈活性上來講,標準的 _Pragma具有更大的靈活性

注意,為什么強調“標準”,我指的是

_Pragma("once")

而不是

#pragma once

以上,可以看出,不管從可移植性、可讀性、便利性,還是規范角度看,_Pragma("once")都有優勢。使用新標準可以提高開發效率,用最自然的思維方式、最簡單的代碼,表達大量內容。

因為前者是語言層面的標準,而后者是編譯器的擴展,可能在標準通過_Pragma操作符之前,GCC就已經實現了#pragma,這個時候,windows,macOS系統的編譯器可能是不認識#pragma的,他可能是別的foo, bar。但是,標準通過之后,所有編譯器都將遵循標準。

所以,使用標準,會是最通用的方式,也是我們的方式。

詳見n3690.pdf:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf

image.png

不過有觀點認為,我們不該使用_Pragma("once"),因為編譯器的實現有bug。(https://stackoverflow.com/questions/1143936/pragma-once-vs-include-guards

我們確立一個標準,提高開發效率的同時,也要確認該標準是否穩定可用,為了防止引入未知的問題。

網上的資料不多,就直接看了下內核、編譯器的實現。

早期存在的問題在于,比如foo.h -> /path/to/foo.h鏈接會共享相同的inode,導致編譯器無法區分。(詳見附錄“內核實現分析”)

在問題相關的上下游,有如下若干事實:
編譯器層面,gcc在3.4.0版本的時候已經修復了上述問題。(詳見附錄“編譯器實現分析”)
開發者層面,絕大多數項目都不會存在上述問題。

這讓我們相信標準,且相信標準的實現。

極端假設,即使編譯器仍存在這個所謂的bug,并且我們刻意觸發了,那么會不會有嚴重問題呢?答案是否定的。因為,它和ifndef造成的macro沖突問題是同一級別的,而且更容易在最早期的編譯階段被發現。換言之,既然你都可以接受ifndef,那么為什么不能接受_Pragma("once")呢,后者即便是在最邪惡的情況下,也不會比前者錯得更多。

結論:我們可以使用標準的_Pragma操作符

大家已經將這個結論,作為我們的共識。在代碼規范中已更新。

具體的編碼,可以直接遵循規范:

如果存在ifndef頭文件保護,那么麻煩進行如下替換:

#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_

改成

_Pragma("once")

如果不存在,請直接使用

_Pragma("once")

內核實現分析(linux-5.7.0):

image.png

編譯器實現分析(gcc-9.2.0):

image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容