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 关注
- 216 浏览
添加回答
举报