精品一区二区三区www,色综合久久久久久久,精品亚洲视频在线观看,久久精品免费一区二区喷潮,久久亚州中文字幕无码毛片

Internet Develppment
互聯(lián)網(wǎng)開(kāi)發(fā)& 推廣服務(wù)提供商

我們擅長(cháng)商業(yè)策略與用戶(hù)體驗的完美結合。

歡迎瀏覽我們的案例。

首頁(yè) > 新聞中心 > 新聞動(dòng)態(tài) > 正文

Go+下個(gè)里程碑:超越cgo,無(wú)縫對接C語(yǔ)言

發(fā)布時(shí)間:2022-04-01 09:18:23來(lái)源:OSC開(kāi)源社區


圖片來(lái)自網(wǎng)絡(luò )/侵刪
  去年(2021年)Go+ 的 slogan 從 “面向數據科學(xué)” 的語(yǔ)言升級到了 “面向工程、STEM 教育與數據科學(xué)” 三位一體的語(yǔ)言。也就是說(shuō),我們希望 Go+ 可以同時(shí)被軟件工程師、中小學(xué)生、數據分析師這三個(gè)截然不同的人群所廣泛使用。

  對 Go+ 來(lái)說(shuō),“面向數據科學(xué)” 這個(gè)目標注定有非常長(cháng)遠的路要走。所以去年 Go+ 的版本迭代主要精力都花在了 “低門(mén)檻” 上。我們努力讓 Go+ 的使用門(mén)檻低到和 Python 相當的水平。這是從 Go+ 作為 “面向 STEM 教育”,作為一種中小學(xué)生就能夠學(xué)習和掌握的教學(xué)語(yǔ)言而做出的努力。我認為這些努力對 Go+ 的發(fā)展來(lái)說(shuō)非常重要。2022 年我們仍然會(huì )繼續去加強 Go+ 在低門(mén)檻方向上的工作。

  但是在去年年底的時(shí)候,我們將 Go+ 下一個(gè)里程碑(v1.2)的版本計劃做了非常巨大的調整:我們從之前的一籮筐的 Go+ 生態(tài)發(fā)展的計劃,調整成為就干一個(gè)非常硬的硬骨頭:實(shí)現對 C 語(yǔ)言的完美支持。

  為什么需要做這個(gè)改變?

  因為到目前為止,Go+ 從 “面向工程” 這個(gè)目標來(lái)說(shuō),它只是 “更好的 Go”??陀^(guān)來(lái)講,這個(gè) “更好” 并不足以讓軟件工程師們心動(dòng)到改變自己的習慣,采用 Go+ 來(lái)進(jìn)行日常的開(kāi)發(fā)工作。這個(gè) Why Go+ 如果回答不好,那么 Go+ 的面向工程這個(gè)目標就僅僅停留于口號。

  Go 語(yǔ)言足夠好,在它擅長(cháng)的服務(wù)端開(kāi)發(fā)領(lǐng)域上,你甚至幾乎可以肯定地說(shuō)它是最好的。但是在其他所有領(lǐng)域,無(wú)論是 PC、Mobile、Web、小程序、嵌入式、區塊鏈與 Web3、大數據與 AI、編程教學(xué)領(lǐng)域等等,Go 都顯得不夠好。如果用學(xué)生來(lái)打比方,Go 像是一個(gè)偏科偏的非常嚴重的學(xué)生,有一門(mén)學(xué)科特別擅長(cháng)考 99 分,但是其它學(xué)科都只是勉強的及格線(xiàn)。

  是什么制約了 Go 語(yǔ)言在其他領(lǐng)域的發(fā)展?

  我們以 Web3 為例。這很可能是與服務(wù)端開(kāi)發(fā)最為接近,Go 最容易取得壓倒性?xún)?yōu)勢的領(lǐng)域。但是實(shí)際的戰績(jì)如何呢?我花時(shí)間對幾十個(gè)頭部的 Web3 項目進(jìn)行了分析,最后的統計結果很意外:在這個(gè)領(lǐng)域 Go 語(yǔ)言的確采用率很高的確沒(méi)錯,但是有另一個(gè)語(yǔ)言 Rust 的采用率也很高,兩者的采用率基本上接近 1:1,甚至一些項目同時(shí)采用 Go 和 Rust。

  為什么會(huì )這樣?在正統的服務(wù)端開(kāi)發(fā)中 Rust 的采用率可能連 Go 的 1/10 都沒(méi)有,為什么一個(gè)看起來(lái)完全類(lèi)似的同樣都是網(wǎng)絡(luò )應用類(lèi)的場(chǎng)景,其采用率居然有這么大的差別?

  我知道一些人把這個(gè)事情歸因到 Go 是 GC 語(yǔ)言,Rust 性能更好這一點(diǎn)上。但我認為這并不成立。如果對網(wǎng)絡(luò )服務(wù)性能是重要的,那么在服務(wù)端開(kāi)發(fā)(尤其是云計算)這樣一個(gè)成本很敏感的領(lǐng)域,Rust 應該比 Go 更有市場(chǎng)競爭力才對。

  我認為最有可能的關(guān)鍵因素是兩個(gè):

  語(yǔ)言帶給人的安全感(更少的安全漏洞);

  C 語(yǔ)言的兼容性。

  Rust 的確能夠帶來(lái)更好的安全感(至少表面上看是這樣),但這個(gè)因素能夠影響到底有多少不得而知。但我認為 C 語(yǔ)言的兼容性是更為重要的因素。Web3 是一個(gè)快賽道,發(fā)展日新月異,大家都會(huì )搶時(shí)間。從搶時(shí)間這個(gè)角度來(lái)看,只對比語(yǔ)言角度 Go 比 Rust 有優(yōu)勢得多。是什么原因讓大家覺(jué)得用 Rust 開(kāi)發(fā)起來(lái)更快?

  因為 Rust 能夠讓軟件工程師們復用大量的 C 語(yǔ)言的社區資源。編程語(yǔ)言史發(fā)展到今天,哪門(mén)語(yǔ)言資源最多我想不用我在這里多費口舌,對于 Web3 這樣一個(gè)日新月異的新領(lǐng)域來(lái)說(shuō),能夠復用既有 C 語(yǔ)言的資源,可以輕易打敗 Go 僅僅靠語(yǔ)言特性本身形成的便捷性?xún)?yōu)勢。

  簡(jiǎn)單一句話(huà),cgo 太雞肋,與 C 語(yǔ)言的兼容上,Go 也就是做到了聊勝于無(wú)而已。

  這里 Web3 只是一個(gè)例子。無(wú)論進(jìn)入到任何服務(wù)端之外的新領(lǐng)域,對 Go 來(lái)說(shuō),兼容 C 都是至關(guān)重要,沒(méi)有之一。

  想清楚了這一點(diǎn),Go+ 面向工程領(lǐng)域的第一個(gè)執行目標就出來(lái)了:實(shí)現對 C 語(yǔ)言的完美兼容。要么讓 cgo 變好,要么提供一個(gè)遠超 cgo 的新的兼容 C 語(yǔ)言的方案。

  這就是去年年底,為什么我們調整 Go+ v1.2 版本的迭代目標的原因。

  目標有了,但是怎么做到呢?剛剛過(guò)去的這個(gè)春節里我幾乎每天一有空就在思考這個(gè)問(wèn)題。為此我查閱了大量的 github 上的代碼,基本上圍繞著(zhù) c2go、c2goasm、cgo、binary/asm2go、bytecode/vm2go 這些話(huà)題。

  最終,我沒(méi)有選擇優(yōu)化 cgo,而是選擇了:c2go。簡(jiǎn)單說(shuō),就是把 C 代碼轉換為 Go 代碼,然后重新用 Go 編譯器進(jìn)行編譯。

  當我把要做 c2go 這個(gè)想法發(fā)到 Go+ 貢獻者群的時(shí)候,大家第一反應都是覺(jué)得不可能,這太難了。

  這條路并非沒(méi)有人走過(guò)。

  大家都知道,Go 團隊自己就做過(guò)這事,當然他們的目標并不是做一個(gè)通用版本的 c2go,而是要實(shí)現 Go 的自舉,把 Go 編譯器(也包括運行時(shí)等)所有的 C 代碼都轉成 Go。所以它的代碼可以針對 Go 編譯器寫(xiě)特殊的轉換規則,只要滿(mǎn)足不別人肉去改編譯器就好。

  第三方c2go 這個(gè)項目。它從 2017 年 2 月開(kāi)始做,到今天有 5 個(gè)年頭了,最初作者雄心勃勃,給自己定了一個(gè)小目標:在沒(méi)有任何人工干預的情況下實(shí)現 sqlite3 到 Go 代碼的自動(dòng)轉換。然而這個(gè)目標至今沒(méi)有實(shí)現。

  但是我認為它的選擇的大方向思路是對的。這個(gè) c2go 項目把整個(gè)轉換過(guò)程分為這樣幾個(gè)步驟:

  首先,用 C 語(yǔ)言的預處理程序(preprocessor)解決掉宏和各種預編譯指令。這樣,我們就只需要處理最純粹的 C 代碼。C 語(yǔ)言的 spec 非常的精簡(jiǎn),比 Go 語(yǔ)言的 spec 還要短小很多,并且每個(gè)語(yǔ)法的功能與 Go 有足夠強的相似性,這意味著(zhù)這一步下來(lái)后,我們工作量相對可控許多。

  這一步是重要的,它意味著(zhù)平臺相關(guān)的工作都在這一步干了,我們后續的步驟不用考慮跨平臺。每個(gè)平臺預處理結果產(chǎn)生的 C 代碼可以不同,從而轉換生成的 Go 代碼也不同,這是可接受的。

  其次,生成的 C 代碼再通過(guò) llvm project 中的 clang 命令行進(jìn)行解析(parser):

  clang -Xclang -ast-dump=json -fsyntax-only [C源文件...]

  這樣就得到 C 語(yǔ)言的抽象語(yǔ)法樹(shù)(C AST)。這一步自己做也可以,但是既然有現成的,能夠先省心就先省心吧,以后有空了再改寫(xiě)不遲(注意 github.com/elliotchance/c2go 這個(gè)項目用的是 -ast-dump 開(kāi)關(guān),而不是 -ast-dump=json,這樣就不得不在再寫(xiě)一個(gè)額外的文本解析代碼來(lái)解析 clang 的輸出)。

  接著(zhù),就是將 C AST 轉為 Go AST。這一步最為核心,我們的大部分工作都集中在這里。類(lèi)似的工作我們在 Go+ 中也已經(jīng)做過(guò)了,只不過(guò)我們之前做的是 Go+ AST 轉為 Go AST 而已??紤] C 語(yǔ)言的 spec 比 Go+ 語(yǔ)言 spec 要小很多,我們心里對工作量就有了大體的估計。

  這一步會(huì )有少量的技術(shù)難點(diǎn),比如 C 的指針是可以移動(dòng)的,比如 C 語(yǔ)言有 union 數據類(lèi)型。但總體來(lái)說(shuō)這些語(yǔ)法都不是特別復雜,工作量是可控的。

  C 是手工管理內存的,這點(diǎn)在轉為 Go 后可以仍然保持不變。C 語(yǔ)言的 malloc 函數可以從 Go 進(jìn)程里面劃出一片內存來(lái)進(jìn)行手工管理。

  還有一些 corner case,比如在 C 代碼里面插入匯編指令的。我們可以考慮引入一個(gè)通用的過(guò)濾機制來(lái)解決:在 c2go 轉換過(guò)程的配置文件中,指定哪些函數或 C 源文件應該被忽略,然后在該工程中加入一個(gè)手工實(shí)現的 Go 源文件來(lái)自己實(shí)現這些被忽略的函數即可。

  在介紹 Go+ 編譯器實(shí)現原理(請移步 bilibili 搜索 "Go+ 公開(kāi)課 · 第1期|Go+ v1.x 的設計與實(shí)現" 進(jìn)行查看)的時(shí)候,我提到過(guò) github.com/goplus/gox 這個(gè)項目,它是用來(lái)輔助生成 Go AST 的模塊。在 C AST 轉為 Go AST 中,我們也會(huì )借助它來(lái)大幅減少開(kāi)發(fā)工作量。

  生成了 Go AST,剩下來(lái)的工作就和我們在 Go+ 一樣了,通過(guò) Go AST 調用 go/format 這個(gè)標準庫來(lái)生成 Go 源文件,最后用 Go 編譯器編譯它。

  在我解釋以上 c2go 的全過(guò)程后,小伙伴們開(kāi)始相信這條路走得通。

  當然,在我內心中,還有一個(gè)更為重要的理由:我們有機會(huì )干得比 cgo 更好。如果選擇優(yōu)化 cgo 的話(huà),是達不到這個(gè)目標的,我們唯一能夠做的只是讓 cgo 更快。

  如何讓 Go+ 對 C 語(yǔ)言的支持更好?

  在 Go+ 與 C 語(yǔ)言的互操作性上,我有以下這些核心的考量準則:

  其一,C 代碼不需要經(jīng)過(guò)額外包裝,可以直接由 Go+ 來(lái)調用。以 Hello world 這個(gè)經(jīng)典程序為例,Go+ 調用 C 標準庫的 printf 代碼如下:

  import "C"

  C.printf(C"Hello, world\n")

  它挺像 cgo,但是它并不是。你可以拿它和cgo 的代碼對比一下:

  package main

  /*

  #include

  #include

  */

  import "C"

  import "unsafe"

  func main() {

  cstr := C.CString("Hello World\n")

  defer C.free(unsafe.Pointer(cstr))

  C.printf(cstr)

  }

  可以看到,在 Go 語(yǔ)言中 import "C" 并不是真導入一個(gè)包,而只是說(shuō)這里有 C 代碼被插入。相比 Go 語(yǔ)言,Go+ 對 C 的支持更像是把 C 看做 Go+ 原生代碼的一部分,而不是外來(lái)者。具體體現在:

  不用寫(xiě)任何 C 代碼就可以引用。C 實(shí)現的模塊如同 Go 實(shí)現的模塊一樣,就是正常的一個(gè) package 而已。import "C" 表示導入 C 語(yǔ)言的標準庫(當然不同平臺不同 C 編譯器的 C 標準庫并不完全相同,這個(gè)考慮在 gop.mod 中對 C 庫這個(gè)模塊進(jìn)行版本綁定)。

  引入了 C 的字符串常量語(yǔ)法 C"...",以增強 C 函數使用上的便捷性。代碼中的 C"Hello, world\n" 等價(jià)于 []int8{'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '\n', '\0'},是一個(gè)以 '\0' 為結尾的 C 字符串。

  那么對于非 C 標準庫,比如類(lèi)似 sqlite3 這樣的知名 C 庫,怎么導入?我們設想是這樣的:

  import "C/sqlite3"

  sqlite3.XXX(...)

  那么如果是用戶(hù)自己實(shí)現的 C 庫呢?嗯,最通用的形式是這樣的:

  import "C/github.com/foo/bar"

  bar.XXX(...)

  很簡(jiǎn)潔,和 Go 語(yǔ)言寫(xiě)的庫看起來(lái)沒(méi)啥大區別,對吧?

  其二,C 與 Go 的類(lèi)型系統盡可能一致,以降低互操作的成本。在 C 語(yǔ)言中的 void (*)() 到 Go 里面就是 func(),兩者屬于同一個(gè) type。其他還有 C 語(yǔ)言的 int 類(lèi)型也是如此。不考慮引入 C.int 這類(lèi)數據結構,它就等同于 Go 語(yǔ)言的 int。為此 Go+ 還會(huì )引入內建的 char 類(lèi)型,實(shí)際上它只是 int8 的別名。

  至于要不要引入 union,我估計大概率 Go+ 不支持 union 概念。但是 C 代碼中定義的 union 類(lèi)型,在 Go+ 中可以識別。比如 sqlite3.XXX 如果是一個(gè) union 類(lèi)型,那就會(huì )有正常的 union 行為,包括可以正確訪(fǎng)問(wèn)其中的成員變量。但是我們不允許在 Go+ 源文件中顯式定義一個(gè) union 類(lèi)型。

  在 C 語(yǔ)言中,函數會(huì )有調用約定(Call Convention)的概念。最常見(jiàn)的調用約定有 cdecl 和 stdcall。其中,cdecl 是 C 函數默認的調用約定,它的好處是支持類(lèi)似 printf 這種允許有不定參數的函數。而 stdcall 是 Windows API 的調用約定,這個(gè)調用方式有時(shí)候會(huì )被叫做 pascal,這當然是因為它與 Pascal 語(yǔ)言的函數調用約定一致的原因。

  這種函數的調用約定的差異在翻譯成 Go 代碼后會(huì )被取消。也就是說(shuō),void (cdecl *)() 和 void (stdcall *)() 這兩種函數指針類(lèi)型在 C 語(yǔ)言里面是兩個(gè)完全不同的類(lèi)型并且無(wú)法相互轉換(強制轉換后進(jìn)行函數調用會(huì )導致程序崩潰),但是翻譯成 Go 后就都是 func(),沒(méi)有區別。

  其三,翻譯成 Go 代碼后的程序語(yǔ)義,也就是 C 程序員對 C 形成的常規的語(yǔ)義理解都仍然正確。包括:C 字符串以 '\0' 為結尾,C 是手工管理內存的等等,這些都仍然不變。如上面已經(jīng)提到過(guò)的那樣,在實(shí)現上 C.malloc 會(huì )從 Go 進(jìn)程中劃出一片內存來(lái)進(jìn)行手工管理。

  最后總結就一句話(huà):我對這個(gè) c2go 項目相當認真。目前它已經(jīng)放到 github 上 goplus 這個(gè)組織里面了。

  我覺(jué)得這事對 Go 未來(lái)十年的生態(tài)繁榮是至關(guān)重要的一項工作,否則 Go 就真的只能偏安在服務(wù)端開(kāi)發(fā)領(lǐng)域了。

  當然從 Go+ 的角度來(lái)說(shuō),它是我們在 “面向工程” 領(lǐng)域的目標下,給軟件工程師們交出的最重要的一份答卷。
 ?。?a href="http://www.does1816.com/wechat/">邯鄲小程序開(kāi)發(fā))

最新資訊
? 2018 河北碼上網(wǎng)絡(luò )科技有限公司 版權所有 冀ICP備18021892號-1   
? 2018 河北碼上科技有限公司 版權所有.
精品一区二区三区www,色综合久久久久久久,精品亚洲视频在线观看,久久精品免费一区二区喷潮,久久亚州中文字幕无码毛片