Goで記述された静的オブジェクトを作成して、Cプログラム(カーネルモジュールなど)とのインターフェイスを作成しようとしています。
GoからC関数を呼び出すことに関するドキュメントを見つけましたが、その逆の方法についてはあまり見つけていません。私が見つけたのは、それは可能だが複雑だということです。
ここに私が見つけたものがあります:
誰もこれを経験したことがありますか?つまり、完全にGoで記述されたPAMモジュールを作成しようとしています。
GoコードをCから呼び出すことができます。しかし、これは混乱を招く提案です。
プロセスの概要は、リンク先のブログ投稿に記載されています。しかし、それがあまり役に立たないことがわかります。これは不要なビットのない短いスニペットです。物事が少し明確になるはずです。
_package foo
// extern int goCallbackHandler(int, int);
//
// static int doAdd(int a, int b) {
// return goCallbackHandler(a, b);
// }
import "C"
//export goCallbackHandler
func goCallbackHandler(a, b C.int) C.int {
return a + b
}
// This is the public function, callable from outside this package.
// It forwards the parameters to C.doAdd(), which in turn forwards
// them back to goCallbackHandler(). This one performs the addition
// and yields the result.
func MyAdd(a, b int) int {
return int( C.doAdd( C.int(a), C.int(b)) )
}
_
すべてが呼び出される順序は次のとおりです。
_foo.MyAdd(a, b) ->
C.doAdd(a, b) ->
C.goCallbackHandler(a, b) ->
foo.goCallbackHandler(a, b)
_
ここで覚えておくべき重要な点は、コールバック関数はGo側では_//export
_コメントで、C側ではextern
としてマークする必要があるということです。これは、使用するコールバックをパッケージ内で定義する必要があることを意味します。
パッケージのユーザーがカスタムコールバック関数を提供できるようにするために、上記とまったく同じアプローチを使用しますが、Cに渡されるパラメーターとしてユーザーのカスタムハンドラー(通常のGo関数)を提供します_void*
_としてサイドします。その後、パッケージ内のコールバックハンドラーによって受信され、呼び出されます。
現在作業中のより高度な例を使用してみましょう。この場合、かなり重いタスクを実行するC関数があります。USBデバイスからファイルのリストを読み取ります。これにはしばらく時間がかかる場合があるため、アプリに進捗を通知する必要があります。これを行うには、プログラムで定義した関数ポインターを渡します。呼び出されるたびに、ユーザーに進行状況情報を表示するだけです。よく知られている署名があるため、独自のタイプを割り当てることができます。
_type ProgressHandler func(current, total uint64, userdata interface{}) int
_
このハンドラーは、ユーザーが保持するために必要なものを保持できるinterface {}値と共に、進行状況情報(現在の受信ファイル数と合計ファイル数)を取得します。
このハンドラーを使用できるようにするには、C and Go配管を記述する必要があります。幸いなことに、ライブラリから呼び出したいC関数により、タイプ_void*
_のuserdata構造体を渡すことができます。これは、保持したいものは何でも保持できることを意味し、質問はされず、そのままGoの世界に戻します。このすべてを機能させるために、Goからライブラリ関数を直接呼び出すのではなく、goGetFiles()
という名前のCラッパーを作成します。このラッパーは、ユーザーデータオブジェクトとともに、実際にGoライブラリをCライブラリに提供します。
_package foo
// #include <somelib.h>
// extern int goProgressCB(uint64_t current, uint64_t total, void* userdata);
//
// static int goGetFiles(some_t* handle, void* userdata) {
// return somelib_get_files(handle, goProgressCB, userdata);
// }
import "C"
import "unsafe"
_
goGetFiles()
関数は、コールバック用の関数ポインターをパラメーターとして受け取らないことに注意してください。代わりに、ユーザーが提供したコールバックは、そのハンドラーとユーザー自身のuserdata値の両方を保持するカスタム構造体にパックされます。これをuserdataパラメーターとしてgoGetFiles()
に渡します。
_// This defines the signature of our user's progress handler,
type ProgressHandler func(current, total uint64, userdata interface{}) int
// This is an internal type which will pack the users callback function and userdata.
// It is an instance of this type that we will actually be sending to the C code.
type progressRequest struct {
f ProgressHandler // The user's function pointer
d interface{} // The user's userdata.
}
//export goProgressCB
func goProgressCB(current, total C.uint64_t, userdata unsafe.Pointer) C.int {
// This is the function called from the C world by our expensive
// C.somelib_get_files() function. The userdata value contains an instance
// of *progressRequest, We unpack it and use it's values to call the
// actual function that our user supplied.
req := (*progressRequest)(userdata)
// Call req.f with our parameters and the user's own userdata value.
return C.int( req.f( uint64(current), uint64(total), req.d ) )
}
// This is our public function, which is called by the user and
// takes a handle to something our C lib needs, a function pointer
// and optionally some user defined data structure. Whatever it may be.
func GetFiles(h *Handle, pf ProgressFunc, userdata interface{}) int {
// Instead of calling the external C library directly, we call our C wrapper.
// We pass it the handle and an instance of progressRequest.
req := unsafe.Pointer(&progressequest{ pf, userdata })
return int(C.goGetFiles( (*C.some_t)(h), req ))
}
_
Cバインディングについては以上です。ユーザーのコードは非常に簡単です。
_package main
import (
"foo"
"fmt"
)
func main() {
handle := SomeInitStuff()
// We call GetFiles. Pass it our progress handler and some
// arbitrary userdata (could just as well be nil).
ret := foo.GetFiles( handle, myProgress, "Callbacks rock!" )
....
}
// This is our progress handler. Do something useful like display.
// progress percentage.
func myProgress(current, total uint64, userdata interface{}) int {
fc := float64(current)
ft := float64(total) * 0.01
// print how far along we are.
// eg: 500 / 1000 (50.00%)
// For good measure, prefix it with our userdata value, which
// we supplied as "Callbacks rock!".
fmt.Printf("%s: %d / %d (%3.2f%%)\n", userdata.(string), current, total, fc / ft)
return 0
}
_
これはすべて、実際よりもずっと複雑に見えます。前の例とは異なり、呼び出し順序は変更されていませんが、チェーンの最後に2つの余分な呼び出しがあります。
順序は次のとおりです。
_foo.GetFiles(....) ->
C.goGetFiles(...) ->
C.somelib_get_files(..) ->
C.goProgressCB(...) ->
foo.goProgressCB(...) ->
main.myProgress(...)
_
Gccgoを使用する場合、混乱を招く提案ではありません。これはここで動作します:
package main
func Add(a, b int) int {
return a + b
}
#include <stdio.h>
extern int go_add(int, int) __asm__ ("example.main.Add");
int main() {
int x = go_add(2, 3);
printf("Result: %d\n", x);
}
all: main
main: foo.o bar.c
gcc foo.o bar.c -o main
foo.o: foo.go
gccgo -c foo.go -o foo.o -fgo-prefix=example
clean:
rm -f main *.o
私に関する限り、それは不可能です。
注:エクスポートを使用している場合、プリアンブルでC関数を定義することはできません。