生成的包含卫兵:一次替代实用主义
介紹
在C ++中,沒有什么可以阻止程序員多次包含頭文件。 這可能會導致定義重復,這是一個錯誤。 由于很難確保只將頭文件包含一次,因此常見的策略是僅對第一個包含計數進行計數。 可以使用“包含保護”來完成,這是一小段預處理器邏輯,如下所示:
它是如何工作的?
在第一個include上, HEADER_HAS_BEEN_INCLUDED未定義,因此我們定義foo 。 在隨后的包含中,已定義HEADER_HAS_BEEN_INCLUDED ,因此我們僅跳過內容。
例如,如果我們有此C ++文件:
然后它將擴展為:
在預處理程序完成后,我們將得到以下結果:
這是慣用的方法,但是有一些限制:
那一次實用主義呢?
#pragma once旨在克服這些問題。 它是C ++編譯器的非標準功能,但得到了廣泛支持。 這個概念很簡單:任何包含#pragma once文件實際上只會被包含一次,即使程序員多次包含它。
#pragma once使用#pragma once ,我們的示例變為:
看起來不錯吧? 可悲的是, #pragma once帶來了許多問題。
根本原因是#pragma once涉及其中一些代碼的生活,而不是它的內容 。 如果您可以通過多個路徑訪問同一文件的兩個副本,那么它將被包含兩次。 而且,如果您有兩條路徑看起來不同但實際上相同,那么編譯器可能不會發現這一點。 最重要的是,它不是標準的,因此編譯器實現不必尊重其語義。
可能的解決方法
#pragma once的問題#pragma once源于它在文件的位置而不是其內容上起作用的事實。 如果我們只是使用內容呢? (當然,記錄每個標題的所有內容會很慢,但是我們可以通過記錄內容的哈希值來進行優化)。
該過程將是:
1.包含頭文件時,對其進行哈希處理
2.如果以前已經看到過哈希,則忽略包含
3.否則,正常包含標題
這將是一個可靠的解決方案,因為它根本不關心文件所在的路徑,而只關心文件的內容。
實施解決方法
將新命令添加到C ++標準將花費大量時間,但是幸運的是,我們可以使用腳本和預處理器來實現此邏輯。
基本思想是這樣的:
因此,例如此標頭:
SHA-256哈希值為:
因此,生成的標頭可能是:
盡管單個文件的轉換很簡單( Python腳本 ),但我們仍然需要管理轉換過程。 我們需要確保:
- 為每個文件運行轉換
- 新文件將自動轉換
- 刪除文件的轉換會自動刪除
- 僅在文件更改后才重新運行轉換
- 獎勵:轉換可以安全地放入共享的網絡緩存中
使用Buck build ,我們可以輕松地將此邏輯編碼到項目的構建腳本中。
讓我們從單個文件的構建規則開始,然后進行概括:
Buck中的genrule很像Make中的目標。 我們定義輸入文件,輸出文件名和要執行的命令。 該目標使用我們的Python腳本來生成包含保護,并在add.hpp上運行它。 與Make不同,Buck將在輸入哈希中隔離并緩存該進程。
現在我們只有一個文件,我們可以將流程推廣到n文件。 為此,我們制作了一個Python函數,該genrule為給定的文件創建了一個genrule :
要獲取頭文件集,我們運行一個glob表達式。 例如:
并將所有內容整合在一起:
您可以在GitHub上找到完整的工作示例 。
現在,我們的頭文件可以一次編寫而無需包含保護或#pragma once :
Buck中的此設置非常適合與以下對象一起使用:
- 頭文件中的零樣板
- Buck會自動檢查新的頭文件,因此構建始終是最新的
- Buck將刪除過時的頭文件
- 因為它了解目標圖,所以Buck將并行生成標頭
- Buck將緩存生成的頭,以便僅在需要時才計算
- 我們不再依賴人類的準確性(包括防護措施)或非標準功能( #pragma once )
既然你在這里...
我們創建了Buckaroo ,以便更輕松地集成C ++庫。 如果您想嘗試一下,最好的起點是文檔 。 您可以瀏覽Buckaroo.pm上的現有軟件包,或在愿望清單上請求更多。
C ++依賴管理的方法,或者為什么我們構建BuckarooC ++是一種不常見的語言,因為它還沒有主要的程序包管理器(我們正在努力!)。 結果是…… hackernoon.com
From: https://hackernoon.com/generated-include-guards-an-alternative-to-pragma-once-31cc3dee6ce
總結
以上是生活随笔為你收集整理的生成的包含卫兵:一次替代实用主义的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android拍照,照片会自己旋转
- 下一篇: 宝宝成长季4天-我出生啦!