C++ 20 的模塊對老式頭文件的兼容性和提高

嘮叨

根據標準,模塊兼容老式頭文件(必須的)……html

若是寫下 import <some_header.h>; 或者 import "some_header.h"; 這樣的 import 語句,那麼針對同一個頭文件來講,它們會做爲同一個隱式的獨立模塊來編譯,而且是原子的。緩存

而,如今的預處理器中,#include <some_header.h> 或者 #include "some_header.h" 則是無視任何 C++ 語義,暴力把文件內容複製粘貼進源文件……(逃模塊化

#include 方式

// hello.cpp

#include "some_header.h"
#include <vector>

int main()
{
    std::vector<int> numbers = { 1, 2, 3, 4, 5 };
    
    cao_dan::fuck(numbers);
}

針對如上源文件,若是我修改了這裏面的任何內容,都會致使頭文件 <vector>some_header.h 的內容被暴力 #include 進 hello.cpp 而後從新編譯一次。工具

若是頭文件的內容,是一堆模板元的話……這編譯速度,你懂的……(逃ui

import 方式

// hello.cpp

import "some_header.h";
import <vector>;

int main()
{
    std::vector<int> numbers = { 1, 2, 3, 4, 5 };
    
    cao_dan::fuck(numbers);
}

這種方式,很好地兼容了老式頭文件。不一樣的是,它把同一個頭文件當成一個模塊單元來編譯,並生成 BMI(標準中叫 Binary Module Interface,即二進制模塊接口)文件,緩存起來。這樣在後續編譯中,若是沒改變頭文件 some_header.h<vector> 的內容的話,構建系統就會直接加載這些 BMI,並連接,而不用從新編譯。(666code

有宏控制的狀況

問題來了:若是老式頭文件中有宏控制怎麼bang?htm

// legacy.h

#pragma once

#ifdef SOME_OPTION_1
// ...
#elif defined(SOME_OPTION_2)
//...
#endif

針對以上頭文件,採用 import "legacy.h" 的方式沒法導入宏,也就沒法讓用戶設置生效。這種狀況,大佬們也考慮到了。推薦的作法是用包裝頭文件實現。接口

// legacy_some_option_1.h

#pragma once

#define SOME_OPTION_1

#include <legacy.h>
// legacy_some_option_2.h

#pragma once

#define SOME_OPTION_2

#include <legacy.h>

這樣 legacy_some_option_1.h 就是針對 SOME_OPTION_1 的設置, legacy_some_option_2.h 就是針對 SOME_OPTION_2 的設置。get

使用以下:編譯器

// main.cpp

import "legacy_some_option_1.h";
// import "legacy_some_option_2.h";

// ...

廢話

針對這個 BMI 以及其派生出來的 C++ 生態系統技術報告,準備在 C++ 20 發佈以前擬定。主要包括一些亟待統一的地方:

  • 模塊 ABI
  • 基於模塊編譯的二進制庫的發佈和使用
  • 針對入門者的友好的模塊化 Hello World
  • 構建系統(如 CMake、MSBuild 等等)
  • 編譯器實現(如 MSVC、Clang、GCC 等)
  • 名稱查找規則(好比一個模塊 boost.asio 怎麼對應到具體的 BMI 文件或源文件,這個規則是什麼?)
  • 模塊世界的包管理工具(急需!)

Over- -

相關文章
相關標籤/搜索