c++20 modules 從量變到質變

今天向大家介紹c++20核心語法modules,從c++11到今天的c++20,這是c++不斷推陳出新從量變到質變的過程。在介紹modules之前,我們先來了解include機制。

include機制

c++為了完全兼容c語言,讓c語言開發者快速學習c++語言,因此繼承了c語言的include機制。include是包含的意思,就是將頭文件的所有內容複製過來,#則代表預處理的意思,相關的預處理還有#ifdef、#endif,在此就不做過多的介紹。

c++將代碼分為頭文件和cpp源文件,頭文件主要是申明函數等相關接口,cpp文件則是具體實現。開發者可以將cpp文件編譯成lib文件,使用者無需關係其具體的實現,這樣能很好的將實現邏輯和接口分離。

什麼是編譯單元

當一個c或cpp文件在編譯時,預處理器首先遞歸包含頭文件,形成一個含有所有必要信息的單個源文件,這個源文件就是一個編譯單元。

編譯單元和頭文件

在同一個編譯單元裡,重複包含頭文件會照成重複定義的問題,例如:一個頭文件定義了一個class(不是申明),然後在一個cpp文件中包含兩次,編譯器就會報“類型重定義”錯誤。為了解決這一編譯錯誤問題,必須要在頭文件裡寫上#ifndef、#define 和 #endif(新版編譯器你可以#pragma once),但大多數人認為這一解決辦法並不完美。

編譯真的很慢很慢

c++編譯慢是公認的,你會發現編譯一個工程的時間足夠幹很多事情,其中有一大部分原因就是include照成的。一個公共的頭文件很可能被N個編譯單元包含,這N個編譯單元都會去編譯頭文件,尤其是修改這個公共頭文件,再編譯簡直讓人慾哭無淚,準確的詮釋了“牽一髮而動全身”。

c++20 modules 從量變到質變

可以看出include的機制有一系列問題,這些問題總是不盡人意,曾經有多少人抱怨include機制太爛,又有多少人為此轉移陣營而投靠其它語言。面對標準委員會的一次又一次推移,最終module特性凍結在c++20。

modules重磅來襲

C++不適應在大規模程序設計與現代開發中的應用環境,越來越多的模板的使用,已經導致了編譯時可伸縮性和程序員生產力的嚴重障礙。它構建性能低下,與雲和分佈式構建的集成性差。此外,嚴重依賴頭文件包含(即從編譯器的角度複製代碼)和宏扼殺了C++開發人員的開發。

為了解決以上系列問題,c++20 modules應運而生!(手動特效:金光閃閃)

c++20 modules 從量變到質變

c++20 modules 從量變到質變

其中,WG21官方的n4465.pdf文檔對modules的目標描述:

1. componentization;

2. isolation from macros;

3. scalable build;

4. support for modern semantics-aware developer tools.

gcc官方對其描述:

Reduce build times due to not reparsing large header files

Proper interface/implementation isolation

Harder to have ODR violations

mvc、gcc、clang 三大編譯器基本已完成c++17的開發工作,目前正在開發c++20核心功能。gcc編譯器很早就建立了module分支,可以從gcc官網看到,2017年3月1日 第一個模塊可執行文件運行。mvc已經開始試驗性的支持module,可以從微軟官方c++博客看到,vs2015就開始支持modules了,我相信正式版本將很快與我們見面。

開始modules工作

如果您使用的是Visual Studio 2017 15.3之前的版本,請添加/experimental:module /module:stdIfcDir "$(VCToolsInstallDir_150)ifc\\$(PlatformTarget)"到`` 配置屬性''-> `` C / C ++''->``命令行''以打開該項目的模塊。

(我的版本是vs2017 15.9.7 ,所以我直接跳過上一步)接下來,項目屬性頁->c/c++->語言->c++語言標準選擇“最新草案標準(/std:c++latest)”,啟用c++模塊(實驗性)選擇“是”。

c++20 modules 從量變到質變

修改屬性頁

//#include <vector>
import std.core;

int main()
{
\tstd::vector vec;
\tvec.push_back(1);
\tvec.push_back(2);
\tvec.push_back(3);

\tfor (auto it = vec.begin(); it != vec.end(); ++it)
\t{
\t\tprintf("%d\\n", *it);
\t}
\tsystem("pause");
\treturn 0;
}
/<vector>

以上是stl的modules示例,微軟官方提供的std模塊參考如下

  • std.regex 提供標題的內容 <regex>
  • std.filesystem 提供標題的內容 <experimental>
  • std.memory 提供標題的內容 <memory>
  • std.threadingprovodes頭部的內容<atomic>,<condition>,<future>,<mutex>,<shared>,<thread>/<shared>/<mutex>/<future>/<condition>/<atomic>
  • std.core 提供C ++標準庫中的所有其他內容

自定義模塊

新建Mo.ixx文件(注意後綴名的變化),然後我們需要在main函數前導入MO模塊(import MO;)並使用它,需要注意的是隻有標記export的才會被導出,否則外部無法調用。我們可以在模塊中導入子模塊,可以用大括號批量export,也可以更小粒度的控制export。

export module MO;
import std.core;
export
{
\tvoid test()
\t{
\t\tprintf("test。。。");
\t}

\tnamespace SPACE
\t{
\t\tclass File
\t\t{
\t\tpublic
\t\t\tvoid process();
\t\t};
\t\t
\t}
}

void test2()
{
\tprintf("test2。。。");
}

void SPACE::File::process()
{

}

編譯它會生成兩個文件,分別是obj和ifc。obj我們並不陌生,那麼ifc是個什麼東西?其實它是模塊的二進制描述文件,無法手動更改,雖然沒有了頭文件,但是需要一個ifc文件,讓編譯器知道接口信息,用於和其它TU建立橋樑。可以看到模塊直接被當做TU處理,無論你有多少個編譯單元import該模塊,都不會重複編譯,大大提高了編譯速度,是不是很酷!

c++20 module 將是一個全新的開始,所帶來改變是巨大的,這些改變是不言而喻的,從這一刻開始構建你的大型項目。好吧,我們扔掉include,從modules開始,更快的構建速度,更清爽的代碼架構,更小的功能依賴,更好的獨立組件,像c#一樣高效而優雅的開發。

結束語

modules終於來了!模塊對於開發的好處毋庸置疑,我相信這是很多人都有所期待的,那些抱怨include垃圾而轉移陣營的同學,是時候迴歸c++了。

從c++11到c++20,還在用c++98的你,我就問你慌不慌?當下急速發展的互聯網時代,不學習就意味著淘汰,希望大家勇於接受新事物!

對此有任何疑問,歡迎大家留言,謝謝!


分享到:


相關文章: