繁体   English   中英

如何从 go 到 python 返回错误字符串

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM