直接嵌入c源代碼到go代碼里面
成都創(chuàng)新互聯(lián)是一家專注于網(wǎng)站設計制作、做網(wǎng)站與策劃設計,秦州網(wǎng)站建設哪家好?成都創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設十年,網(wǎng)設計領域的專業(yè)建站公司;建站業(yè)務涵蓋:秦州等地區(qū)。秦州做網(wǎng)站價格咨詢:18980820575
package main
/*
#include stdio.h
void myhello(int i) {
printf("Hello C: %d\n", i);
}
*/
import "C"
import "fmt"
func main() {
C.myhello(C.int(12))
fmt.Println("Hello Go");
}
需要注意的是C代碼必須放在注釋里面
import "C"語句和前面的C代碼之間不能有空行
運行結果
$ go build main.go ./main
Hello C: 12
Hello Go
分開c代碼到單獨文件
嵌在一起代碼結構不是很好看,很多人包括我,還是喜歡把兩個分開,放在不同的文件里面,顯得干凈,go源文件里面是go的源代碼,c源文件里面是c的源代碼。
$ ls
hello.c hello.h main.go
$ cat hello.h
void hello(int);
$ cat hello.c
#include stdio.h
void hello(int i) {
printf("Hello C: %d\n", i);
}
$ cat main.go
package main
// #include "hello.h"
import "C"
import "fmt"
func main() {
C.hello(C.int(12))
fmt.Println("Hello Go");
}
編譯運行
$ go build ./main
Hello C: 12
Hello Go
編譯成庫文件
如果c文件比較多,最好還是能夠編譯成一個獨立的庫文件,然后go來調用庫。
$ find mylib main
mylib
mylib/hello.h
mylib/hello.c
main
main/main.go
編譯庫文件
$ cd mylib
# gcc -fPIC -shared -o libhello.so hello.c
編譯go程序
$ cd main
$ cat main.go
package main
// #cgo CFLAGS: -I../mylib
// #cgo LDFLAGS: -L../mylib -lhello
// #include "hello.h"
import "C"
import "fmt"
func main() {
C.hello(C.int(12))
fmt.Println("Hello Go");
}
$ go build main.go
運行
$ export LD_LIBRARY_PATH=../mylib
$ ./main
Hello C: 12
Hello Go
在我們的例子中,庫文件是編譯成動態(tài)庫的,main程序鏈接的時候也是采用的動態(tài)庫
$ ldd main
linux-vdso.so.1 = (0x00007fffc7968000)
libhello.so = ../mylib/libhello.so (0x00007f513684c000)
libpthread.so.0 = /lib64/libpthread.so.0 (0x00007f5136614000)
libc.so.6 = /lib64/libc.so.6 (0x00007f5136253000)
/lib64/ld-linux-x86-64.so.2 (0x000055d819227000)
理論上講也是可以編譯成整個一靜態(tài)鏈接的可執(zhí)行程序,由于我的機器上缺少靜態(tài)鏈接的系統(tǒng)庫,比如libc.a,所以只能編譯成動態(tài)鏈接。
當客戶端在 發(fā)出POST請求時/albums,您希望將請求正文中描述的專輯添加到現(xiàn)有專輯數(shù)據(jù)中。
為此,您將編寫以下內容:
1、編寫代碼
a.添加代碼以將專輯數(shù)據(jù)添加到專輯列表。
在此代碼中:
1)用于Context.BindJSON 將請求正文綁定到newAlbum。
2) album將從 JSON 初始化的結構附加到albums 切片。
3)向響應添加201狀態(tài)代碼,以及表示您添加的專輯的 JSON。
b.更改您的main函數(shù),使其包含該router.POST函數(shù),如下所示。
在此代碼中:
1)將路徑中的POST方法與 /albumspostAlbums函數(shù)相關聯(lián)。
使用 Gin,您可以將處理程序與 HTTP 方法和路徑組合相關聯(lián)。這樣,您可以根據(jù)客戶端使用的方法將發(fā)送到單個路徑的請求單獨路由。
a.如果服務器從上一節(jié)開始仍在運行,請停止它。
b.從包含 main.go 的目錄中的命令行,運行代碼。
c.從不同的命令行窗口,用于curl向正在運行的 Web 服務發(fā)出請求。
該命令應顯示添加專輯的標題和 JSON。
d.與上一節(jié)一樣,使用curl檢索完整的專輯列表,您可以使用它來確認添加了新專輯。
該命令應顯示專輯列表。
當客戶端向 發(fā)出請求時GET /albums/[id],您希望返回 ID 與id路徑參數(shù)匹配的專輯。
為此,您將:
a.在您在上一節(jié)中添加的函數(shù)下方postAlbums,粘貼以下代碼以檢索特定專輯。
此getAlbumByID函數(shù)將提取請求路徑中的 ID,然后找到匹配的專輯。
在此代碼中:
(1)Context.Param用于從 URL 中檢索id路徑參數(shù)。當您將此處理程序映射到路徑時,您將在路徑中包含參數(shù)的占位符。
(2)循環(huán)album切片中的結構,尋找其ID 字段值與id參數(shù)值匹配的結構。如果找到,則將該album結構序列化為 JSON,并將其作為帶有200 OK HTTP 代碼的響應返回。
如上所述,實際使用中的服務可能會使用數(shù)據(jù)庫查詢來執(zhí)行此查找。
(3)如果找不到專輯,則返回 HTTP 404錯誤。
b.最后,更改您的main,使其包含對router.GET的新調用,路徑現(xiàn)在為/albums/:id ,如以下示例所示。
在此代碼中:
(1)將/albums/:id路徑與getAlbumByID功能相關聯(lián)。在 Gin 中,路徑中項目前面的冒號表示該項目是路徑參數(shù)。
a.如果服務器從上一節(jié)開始仍在運行,請停止它。
b.在包含 main.go 的目錄中的命令行中,運行代碼以啟動服務器。
c.從不同的命令行窗口,用于curl向正在運行的 Web 服務發(fā)出請求。
該命令應顯示您使用其 ID 的專輯的 JSON。如果找不到專輯,您將收到帶有錯誤消息的 JSON。
恭喜!您剛剛使用 Go 和 Gin 編寫了一個簡單的 RESTful Web 服務。
本節(jié)包含您使用本教程構建的應用程序的代碼。
直接調用so的函數(shù)cgo應該繞不開吧,我寫過一個銀行的應用程序調用其特色業(yè)務接口,因為接口只支持c和java,我就封裝了一個c的so,然后用cgo調用后寫了一個RPC供遠程的go語言調用,因為RPC只負責信息交互不負責業(yè)務邏輯,所以寫了不到百行,以后基本不用再改。記住雖然go語言自帶gc,但cgo還是要手工釋放內存哦。
Cgo 使得Go程序能夠調用C代碼. cgo讀入一個用特別的格式寫的Go語言源文件, 輸出Go和C程序, 使得C程序能打包到Go語言的程序包中.
舉例說明一下. 下面是一個Go語言包, 包含了兩個函數(shù) -- Random 和 Seed -- 是C語言庫中random和srandom函數(shù)的馬甲.
package rand
/*
#include stdlib.h
*/ import "C" func Random() int { return int(C.random()) } func Seed(i int) { C.srandom(C.uint(i)) }
我們來看一下這里都有什么內容. 開始是一個包的導入語句.
rand包導入了"C"包, 但你會發(fā)現(xiàn)在Go的標準庫里沒有這個包. 那是因為C是一個"偽包", 一個為cgo引入的特殊的包名, 它是C命名空間的一個引用.
rand 包包含4個到C包的引用: 調用 C.random和C.srandom, 類型轉換 C.uint(i)還有引用語句.
Random函數(shù)調用libc中的random函數(shù), 然后回返結果. 在C中, random返回一個C類型的長整形值, cgo把它輪換為C.long. 這個值必需轉換成Go的類型, 才能在Go程序中使用. 使用一個常見的Go類型轉換:
func Random() int { return int(C.random()) }
這是一個等價的函數(shù), 使用了一個臨時變量來進行類型轉換:
func Random() int { var r C.long = C.random() return int(r) }
Seed函數(shù)則相反. 它接受一個Go語言的int類型, 轉換成C語言的unsigned int類型, 然后傳遞給C的srandom函數(shù).
func Seed(i int) { C.srandom(C.uint(i)) }
需要注意的是, cgo中的unsigned int類型寫為C.uint; cgo的文檔中有完整的類型列表.
這個例子中還有一個細節(jié)我們沒有說到, 那就是導入語句上面的注釋.
/*
#include stdlib.h
*/ import "C"
Cgo可以識別這個注釋, 并在編譯C語言程序的時候將它當作一個頭文件來處理. 在這個例子中, 它只是一個include語句, 然而其實它可以是使用有效的C語言代碼. 這個注釋必需緊靠在import "C"這個語句的上面, 不能有空行, 就像是文檔注釋一樣.
Strings and things
與Go語言不同, C語言中沒有顯式的字符串類型. 字符串在C語言中是一個以0結尾的字符數(shù)組.
Go和C語言中的字符串轉換是通過C.CString, C.GoString,和C.GoStringN這些函數(shù)進行的. 這些轉換將得到字符串類型的一個副本.
下一個例子是實現(xiàn)一個Print函數(shù), 它使用C標準庫中的fputs函數(shù)把一個字符串寫到標準輸出上:
package print // #include stdio.h // #include stdlib.h import "C" import "unsafe" func Print(s string) { cs := C.CString(s) C.fputs(cs, (*C.FILE)(C.stdout)) C.free(unsafe.Pointer(cs)) }
在C程序中進行的內存分配是不能被Go語言的內存管理器感知的. 當你使用C.CString創(chuàng)建一個C字符串時(或者其它類型的C語言內存分配), 你必需記得在使用完后用C.free來釋放它.
調用C.CString將返回一個指向字符數(shù)組開始處的指錯, 所以在函數(shù)退出前我們把它轉換成一個unsafe.Pointer(Go中與C的void 等價的東西), 使用C.free來釋放分配的內存. 一個慣用法是在分配內存后緊跟一個defer(特別是當這段代碼比較復雜的時候), 這樣我們就有了下面這個Print函數(shù):
func Print(s string) { cs := C.CString(s) defer C.free(unsafe.Pointer(cs)) C.fputs(cs, (*C.FILE)(C.stdout)) }
構建 cgo 包
如果你使用goinstall, 構建cgo包就比較容易了, 只要調用像平常一樣使用goinstall命令, 它就能自動識別這個特殊的import "C", 然后自動使用cgo來編譯這些文件.
如果你想使用Go的Makefiles來構建, 那在CGOFILES變量中列出那些要用cgo處理的文件, 就像GOFILES變量包含一般的Go源文件一樣.
rand包的Makefile可以寫成下面這樣:
include $(GOROOT)/src/Make.inc
TARG=goblog/rand
CGOFILES=\ rand.go\ include $(GOROOT)/src/Make.pkg
然后輸入gomake開始構建.
更多 cgo 的資源
cgo的文檔中包含了關于C偽包的更多詳細的說明, 以及構建過程. Go代碼樹中的cgo的例子給出了更多更高級的用法.
一個簡單而又符合Go慣用法的基于cgo的包是Russ Cox寫的gosqlite. 而Go語言的網(wǎng)站上也列出了更多的的cgo包.
最后, 如果你對于cgo的內部是怎么運作這個事情感到好奇的話, 去看看運行時包的cgocall.c文件的注釋吧.
分享名稱:go語言調用capi go語言調用c接口
文章網(wǎng)址:http://www.rwnh.cn/article24/hheoce.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、響應式網(wǎng)站、品牌網(wǎng)站設計、面包屑導航、做網(wǎng)站、全網(wǎng)營銷推廣
聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)