簡體   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