cgo コールバック (関数ポインタ型の利用)
概要
本サンプルは、31.C_callback の補足となるサンプルです。
31.C_callback では、Goの関数ポインタをCに渡す際の一般的なイディオムとして、(*[0]byte) への直接キャストを利用しました。本サンプルでは、C側で関数ポインタの typedef を定義し、それを利用することで、よりcgoの型変換の仕組みに沿った形で関数ポインタを取得する方法を示します。
最終的にCの関数に渡される値は 31.C_callback と同じですが、そこに至るまでの過程が異なります。
31.C_callback との違い
一番の違いは、Cのコードブロック内で関数ポインタの型を typedef で定義している点です。
typedef int (*callback_fn)(int, int);
cgoは、このように typedef で定義された関数ポインタ型 callback_fn を、Go側では *[0]byte 型として特別に扱います。そして、C.callback_fn というGoの型としても利用できるようになります。
コードのポイント
main.go では、以下の手順で関数ポインタを取得しています。
-
//export されたGoの関数 export_func は、cgoによって unsafe.Pointer 型の C.export_func という変数として提供されます。
var fn unsafe.Pointer = C.export_func
-
Cで typedef した callback_fn を使い、unsafe.Pointer 型の fn をキャストします。cgoのルールにより、C.callback_fn は *[0]byte 型として解釈されるため、結果として p は *[0]byte 型のポインタになります。
var p *[0]byte = C.callback_fn(fn)
-
この p は、31.C_callback で見た (*[0]byte)(C.export_func) という直接的なキャストと全く同じ値になります。
var p2 = (*[0]byte)(C.export_func) // 上の過程を一行で書いたものと同じ
このように、typedef を経由する方法は、cgoが内部で行う型変換をより明示的にコードに表現する方法と言えます。どちらの方法も有効ですが、このサンプルはcgoの挙動をより深く理解する助けとなります。
実行方法
go run .
実行結果
-------------------------------
[C ] x=2, y=3
[Go] sleep 1sec
[Go] x=2, y=3, ans=6
[C ] ans=6
-------------------------------
[C ] x=2, y=3
[Go] sleep 1sec
[Go] x=2, y=3, ans=6
[C ] ans=6
[MAIN] 6:6