1. 程式人生 > >python呼叫golang並回調

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