天天看點

python調用golang并回調

最近折騰python互動,也真夠嗆的,一連玩了好幾天,被虐的不要不要的。天天各種百度,Google之間。

好吧,廢話少說,轉入我們的正題。其實,py調用go一般的函數,隻是第一道坎,正主其實是py調用go,并且go還回調py!!!

網上其實這些問題很少,而且有且隻有一篇關于go回調py的。

就是如下一位大兄弟寫的:https://www.golangtc.com/t/59f858c04ce40d3bf47f5fbc

雖說他是說解決了,問題是下面的解決方案寫的真心有問題啊。。。其實,py調用go,他們是通過c來進行橋接(應該是這麼說吧),py<——>c<——>go,就是說,py一直認為自己是調用c,go也是如此,并不知其實他們是在互相操作。。。

那麼,好辦了,py調用go并且回調,在py側,隻要按照py調用c,并且回調就可以了。go側則go調用c,并且回調c,就可以了。

其實py側很簡單,随便百度一下,應該是正确的。上面那大兄弟寫的方法就可以了。問題是go側,真心坑。。。

當py傳入自己的回調,其實是被c包裝了一下,然後,go這邊接收的其實就是一個c的函數指針!在go裡面,c的函數指針,其實就是一個unsafe.Pointer,一個unsafe.Pointer,一個unsafe.Pointer,重要的事情說三次!!!但go獲得這東西,它隻知道是一個位址啊,不知道是一個什麼東西。。。好吧,隻能把它重新轉回c的函數指針,但這個過程必須要靠一個c函數做過渡!!!!

然而,這樣就出現一個坑了!!!那個c函數定義,居然不能跟導出的go函數寫在同一個go檔案裡面!!!否則,會一直報重複定義的錯誤,呵呵。于是乎,隻能這麼弄,分三個檔案:一個.h檔案,兩個.go檔案。

clib.h

#ifndef CLIB_H
#define CLIB_H
typedef void (*callback)(int);
#endif           

複制

這是定義回調結構的。

clibh.go

package main

/*
#include "clib.h"
void TestCCB(int c, callback cb){
    cb(c);
}
*/
import "C"           

複制

這是定義go調用c函數的,而且這個必須要有,用來間接調用c回調(py回調)的。

main.go

package main

/*
#include "clib.h"
extern void TestCCB(int c, callback cb);
*/
import "C"
import (
   "unsafe"
)

//export TestCB
func TestCB(a, b int, cb unsafe.Pointer){
   c:=a+b
   
   C.TestCCB(C.int(c), (*[0]byte)(cb))
}

func main() {
}           

複制

然後這個,是導出我們的正主:TestCB。其中的參數,cb就是針對c(py)回調的,在函數體裡面,其實用TestCCB(中間c函數)來調用這個回調,注意:上方extern void TestCCB(int c, callback cb);隻能這麼弄了,不能直接在這個.go檔案寫它的定義。我就是為此折騰了好些天的,直接在裡面定義c中間函數,就直接報重複定義了。

然後,編譯指令要注意了:

go build -ldflags=-s -buildmode=c-shared -o foo.so clibh.go main.go           

複制

兩個.go檔案,必須要寫出來!就是上面那大兄弟說的連個go,當時我看着他說的這個名詞,愣了半天,不知他說啥。。。其實就是把該編譯的go都寫吧。。。不知為啥go編譯的時候,不會主動把同一個包的代碼都編譯在一起。。。随便,能用就行。。。

接下來,在py側,就很簡單了。。。

import ctypes

lib = ctypes.CDLL('./foo.so')

CGOFunc = ctypes.CFUNCTYPE(None, ctypes.c_int32)

def GoCB(c):
    print("c =", c)

cb = CGOFunc(GoCB)
lib.TestCB(5, 6, cb)           

複制

這個網上都有說,其實就是py調用c接口,然後c又回調py函數的做法。。。以上。

參考了幾個網址:

https://www.golangtc.com/t/59f858c04ce40d3bf47f5fbc

https://github.com/golang/go/wiki/cgo#function-pointer-callbacks

https://xiaowing.github.io/post/howto_call_a_go_func_via_funcpoint_from_cside/

https://studygolang.com/articles/2629