[英]How to return an error string from go to python
我正在 Go(c 共享)中编写一个共享的 object,它将从 python 加载和运行。 一切正常,直到 Go 代码需要返回错误。 我正在使用 error.Error() 将错误转换为字符串,但是当尝试将其返回到 python 时,cgo 正在命中:
panic: runtime error: cgo result has Go pointer
这很奇怪,因为这是一个字符串,而不是一个指针。 我知道通过共享的 object 导出的 function 返回 go 字符串没有问题,因为我在其他几个地方这样做没有任何问题。
Go 代码如下所示:
package main
import "C"
//export MyFunction
func MyFunction() string {
err := CallSomethingInGo()
if err != nil {
return err.Error()
}
return ""
}
func main() {}
go 代码使用 buildmode=c-shared 编译为.so,然后在 python 代码中,我有这样的东西:
from ctypes import *
lib = cdll.LoadLibrary("./mygocode.so")
class GoString(Structure):
_fields_ = [("p", c_char_p),("n", c_longlong)]
theFunction = lib.MyFunction
theFunction.restype = GoString
err = theFunction()
当最后一行执行并且 golang 代码返回 NO 错误时,一切都很好并且可以正常工作,但是。 如果 golang 代码尝试返回错误(例如:CallSomethingInGo 失败并返回 err),则 python 代码将失败并显示:
panic: runtime error: cgo result has Go pointer
我尝试手动将字符串从 go 返回到 python 并且它工作正常,但尝试返回 error.Error() (根据我的理解应该是一个字符串)失败。 将错误的字符串表示形式返回给 python 的正确方法是什么?
还有一条信息-来自golang,我做了一个 printf("%T", err) ,我看到错误的类型是:
*os.PathError
我也做了 printf("%T", err.Error()) 并确认 err.Error() 返回的类型是 'string' 所以我仍然不确定为什么这不起作用。
对我来说甚至更陌生...我尝试修改 go 函数进行测试,如下所示,此代码工作正常并将“test”作为字符串返回给 python...
//export MyFunction
func MyFunction() string {
err := CallSomethingInGo()
if err != nil {
// test
x := errors.New("test")
return x.Error()
}
return ""
}
我很困惑,该测试如何工作。 但不是 err?Error() ?
正如我在评论中所说,您只是不允许这样做。
Cgo 文档中概述了从 C 代码调用 Go 代码的规则,本节中描述了这个特殊问题,以这种方式(尽管我特别加粗了一些部分):
传递指针
Go 是一种垃圾收集语言,垃圾收集器需要知道每个指向 Go memory 的指针的位置。 因此,在 Go 和 C 之间传递指针存在限制。
In this section the term Go pointer means a pointer to memory allocated by Go (such as by using the & operator or calling the predefined new function) and the term C pointer means a pointer to memory allocated by C (such as by a call to C.malloc)。 指针是 Go 指针还是 C 指针是由 memory 的分配方式决定的动态属性; 它与指针的类型无关。
请注意,某些 Go 类型的值,除了类型的零值外,始终包含 Go 指针。 这适用于字符串、切片、接口、通道、map 和 function 类型。 指针类型可以包含 Go 指针或 C 指针。 数组和结构类型可能包括也可能不包括 Go 指针,具体取决于元素类型。 下面关于 Go 指针的所有讨论不仅适用于指针类型,还适用于包括 Go 指针在内的其他类型。
Go code may pass a Go pointer to C provided the Go memory to which it points does not contain any Go pointers. C 代码必须保留此属性:它不能在 Go ZCD691B4957F0918CD8D86 中临时存储任何 Go 指针。 当传递指向结构中字段的指针时,问题中的 Go memory 是该字段占用的 memory,而不是整个结构。 当传递指向数组或切片中元素的指针时,所讨论的 Go memory 是切片的整个数组或整个后备数组。
调用返回后,C 代码可能不会保留 Go 指针的副本。 这包括 _GoString_ 类型,如上所述,它包括一个 Go 指针; C 代码可能不会保留 _GoString_ 值。
A Go function called by C code may not return a Go pointer (which implies that it may not return a string, slice, channel, and so forth). A Go function called by C code may take C pointers as arguments, and it may store non-pointer or C pointer data through those pointers, but it may not store a Go pointer in memory pointed to by a C pointer. A Go function called by C code may take a Go pointer as an argument, but it must preserve the property that the Go memory to which it points does not contain any Go pointers.
Go 代码可能不会在 C ZCD69B4957F06CD818D7B3D691 中存储 Go 指针 C code may store Go pointers in C memory, subject to the rule above: it must stop storing the Go pointer when the C function returns.
这些规则在运行时动态检查。 检查由 GODEBUG 环境变量的 cgocheck 设置控制。 默认设置是 GODEBUG=cgocheck=1,它实现了相当便宜的动态检查。 可以使用 GODEBUG=cgocheck=0 完全禁用这些检查。 通过 GODEBUG=cgocheck=2 可以在运行时完成对指针处理的检查,但需要付出一定的代价。
可以通过使用不安全的 package 来击败这种强制执行,当然没有什么可以阻止 C 代码做任何它喜欢的事情。 但是,违反这些规则的程序很可能会以意想不到和不可预知的方式失败。
这就是您所看到的:您有一个程序违反了多个规则,现在它以意想不到和不可预知的方式失败。 特别是,您的lib.MyFunction
是
由 C 代码调用的 Go function
因为 Python 的cdll
处理程序算作 C 代码。 您可以返回nil
,因为那是零值,但不允许您返回 Go 字符串。 在运行时未捕获空字符串常量(以及来自其他一些错误类型的其他字符串常量)的事实是运气问题。 1
1这是好运还是坏运,取决于你的观点。 如果它一直失败,也许你会更早地查阅 Cgo 文档。 相反,它会意外失败,但在最常见的情况下不会。 这里发生的是字符串常量被编译为文本(或rodata)部分,因此实际上并不是动态分配的。 然而,一些——不是全部,而是一些——错误的字符串字节是动态分配的。 一些os.PathError
指向 GC-able memory,这些是被
相当便宜的动态检查
在倒数第二段中提到。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.