1 回答

TA貢獻2051條經驗 獲得超10個贊
CGo 允許您將 Go 代碼鏈接到實現 C 風格外部函數接口的代碼。這并不意味著您可以將任意語言代碼固定到位。
讓我們從第一個問題開始,即import "C"您的一個 Go 文件中的行必須僅包含其上方的 C 代碼。那是:
/*
#include <stdlib.h>
extern char *cstyle_plus();
*/
可以,但是:
/*
#include <stdlib.h>
extern std::string *plus();
*/
不是,您也不能#include在這里使用任何 C++ 標頭。為了稍微簡化事情,這里的注釋實際上被剪掉并提供給C 編譯器。如果它不是有效的 C,它就不會編譯。
如果你想包含 C++ 代碼,你可以,但你必須將它放在一個或多個單獨的文件中(從技術上講,C 或 C++ 術語中的“翻譯單元”)。然后 CGo 將該文件編譯為目標代碼。
然而,下一個問題是目標代碼必須符合CGo 實現的 C外部函數接口。這意味著您的 C++ 代碼必須返回 C 類型(和/或接收此類類型作為參數)。由于std::string不是 C 字符串,您實際上不能直接返回它。
它不是很有效(并且存在一些解決此問題的嘗試),但處理此問題的常用方法是讓 C 函數返回 C 風格的“char *”或“const char *”字符串。如果字符串本身具有非靜態持續時間——就像你的那樣——你必須malloc在這里使用,特別是C malloc(std::malloc可能是不可互操作的)。
函數本身也必須可以從C 代碼調用。這意味著我們需要使用extern "C"它。
因此,我們的plus.cpp文件(或任何你想稱呼它的東西)可能會這樣讀:
#include <stdlib.h>
#include <iostream>
std::string plus() {
return "Hello World!\n";
}
extern "C" {
char *cstyle_plus() {
// Ideally we'd use strdup here, but Windows calls it _strdup
char *ret = static_cast<char *>(malloc(plus().length() + 1));
if (ret != NULL) {
strcpy(ret, plus().c_str());
}
return static_cast<char *>(ret);
}
}
然后我們可以使用這個從 Go 中調用它main.go:
package main
/*
#include <stdlib.h>
extern char *cstyle_plus();
*/
import "C"
import (
"fmt"
"unsafe"
)
func Plus_go() string {
s := C.cstyle_plus()
defer C.free(unsafe.Pointer(s))
return C.GoString(s)
}
func main() {
a := Plus_go()
fmt.Println(a)
}
添加一個 trivialgo.mod和 building,生成的代碼運行;雙換行是因為C字符串里面有一個換行符,加fmt.Println了一個換行符:
$ go build
$ ./cgo_cpp
Hello World!
這段代碼有點草率:應該malloc失敗,它返回 NULL,并將C.GoString其變成一個空字符串。然而,真正的代碼應該盡量避免這種愚蠢的分配和釋放序列:例如,我們可能知道字符串長度,或者有一個static不需要這種愚蠢的字符串malloc。
- 1 回答
- 0 關注
- 344 瀏覽
添加回答
舉報