やりたいこと
下図のようにGoで記述した関数をPythonから呼び出します。
まずはGoプログラムをビルドして共有ライブラリ(※)を作成し、ビルドした共有ライブラリをPythonから呼び出します。
※ Linuxだと共有ライブラリ.so
、Windowsだとダイナミックライブラリ.dll
。
1. 共有ライブラリを作成
まずはGoでスクリプトを記述します。
記述する上でのポイントは以下です。
- package名はmainにする
- main関数を記述する
- 特殊コメントの
export
を記述する
// package名はmainにする
package main
import (
"fmt"
"C"
)
func main() {
// main関数を記述
}
//export HelloWorld
func HelloWorld() {
fmt.Println("Hello World")
}
//export AddNum
func AddNum (x1,x2 C.int) C.int {
result := int(x1)+int(x2)
return C.int(result)
}
//export AddStr
func AddStr (s1,s2 *C.char) *C.char {
result := C.GoString(s1)+C.GoString(s2)
return C.CString(result)
}
ここでインポートしている"C"パッケージですが、Goにはそのような名前のパッケージは存在しません。
C? Go? Cgo!の記事には以下のように記載されています。
C is a “pseudo-package”, a special name interpreted by cgo as a reference to C’s name space.
共有ライブラリの作成は以下のコマンドにより行います。
これによりtest.so
という名前のファイルが作成されます。
go build -buildmode=c-shared -o test.so test.go
共有ライブラリに記述した関数が含まれていることを確認します。
test.so
のあるフォルダで以下のnmコマンド
を実行します。
実行結果にAddNumなど、先ほど記述した関数の名前が含まれていることが確認できます。
[Go] $ nm test.so | grep " T "
00000000000ab480 T _AddNum
00000000000ab4e0 T _AddStr
00000000000ab440 T _HelloWorld
00000000000ab540 T __cgo_0e9602f8d2bc_Cfunc__Cmalloc
00000000000ab920 T __cgo_get_context_function
000000000006a4c0 T __cgo_panic
00000000000ab580 T __cgo_release_context
00000000000ab620 T __cgo_sys_thread_start
00000000000668c0 T __cgo_topofstack
00000000000ab790 T __cgo_try_pthread_create
00000000000ab830 T __cgo_wait_runtime_init_done
00000000000ab380 T __cgoexp_0e9602f8d2bc_AddNum
00000000000ab3e0 T __cgoexp_0e9602f8d2bc_AddStr
00000000000ab320 T __cgoexp_0e9602f8d2bc_HelloWorld
000000000006a520 T _crosscall2
00000000000aba30 T _crosscall_amd64
00000000000ab980 T _x_cgo_callers
00000000000ab5b0 T _x_cgo_init
00000000000ab8b0 T _x_cgo_notify_runtime_init_done
00000000000ab8f0 T _x_cgo_set_context_function
00000000000ab950 T _x_cgo_setenv
00000000000ab700 T _x_cgo_sys_thread_create
00000000000ab9e0 T _x_cgo_thread_start
00000000000ab970 T _x_cgo_unsetenv
2. Pythonから呼び出す
作成した共有ライブラリをPythonから使用するにはctypes
モジュールを使用します。
引数を渡して結果を受け取るにはargtypes
およびrestype
でそれらの型を指定します。
実装例は以下のようになります。
from ctypes import *
# 共有ライブラリの読み込み
lib = cdll.LoadLibrary("test.so")
# HelloWorld関数を使用
f = lib.HelloWorld
f()
# AddNum関数を使用
f = lib.AddNum
f.argtypes = [c_int, c_int]
f.restype = c_int
result = f(1, 2)
print("AddNum :", result)
# lib.AddStr関数を使用
f = lib.AddStr
f.argtypes = [c_char_p, c_char_p]
f.restype = c_char_p
result = f(b"Hoge", b"Piyo")
print("AddStr :", result)
作成したpythonスクリプトを実行すると、以下の通りPython側で結果を受け取れていることが確認できます。
[Go] $ python test.py
Hello World
AddNum : 3
AddStr : b'HogePiyo'