[解決済み] C言語から囲碁の関数を呼び出す
質問
Cプログラム(例えば、カーネルモジュールか何か)とインターフェースするために、Goで書かれた静的オブジェクトを作成しようとしています。
GoからCの関数を呼び出すためのドキュメントは見つけましたが、その逆の方法についてはあまり見つけられませんでした。 私が見つけたのは、それは可能だが、複雑だということです。
以下は、私が見つけたものです。
どなたか経験のある方はいらっしゃいますか?要するに、私は完全にGoで書かれたPAMモジュールを作ろうとしているのです。
どのように解決するのですか?
C言語からGoのコードを呼び出すことができます。しかし、それは紛らわしい提案です。
リンク先のブログ記事にその手順が紹介されています。でも、あまり参考にならないのは目に見えています。ここに、不要な部分を除いた短いスニペットがあります。これで少しは分かりやすくなるはずです。
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)
ここで覚えておくべきことは、コールバック関数には
//export
というコメントがあり、Go 側では
extern
をC言語で表示します。つまり、使用したいコールバックはパッケージ内で定義する必要があります。
パッケージのユーザがカスタムコールバック関数を提供できるようにするには、上記とまったく同じアプローチを使用しますが、ユーザのカスタムハンドラ(これは単なる通常のGo関数です)をパラメータとして提供し、C側で
void*
. そして、パッケージ内のcallbackhandlerがそれを受け取り、呼び出されます。
私が現在取り組んでいる、より高度な例を使ってみましょう。この場合、かなり重いタスクを実行するC関数があります:それは、USBデバイスからファイルのリストを読み取ることです。これは時間がかかるので、アプリに進行状況を通知するようにします。これは、プログラム内で定義した関数ポインタを渡すことで実現できます。この関数は、呼び出されるたびにユーザーに進行状況を表示します。この関数はよく知られたシグネチャを持っているので、独自の型を割り当てることができます。
type ProgressHandler func(current, total uint64, userdata interface{}) int
このハンドラは、進捗情報(現在の受信ファイル数と合計ファイル数)と、ユーザーが必要とするあらゆるものを保持できるinterface{}の値を受け取ります。
さて、このハンドラを使えるようにするために、C言語とGoのプラグインを書く必要がある。幸運なことに、ライブラリから呼び出したい C の関数では、ユーザデータ構造体として
void*
. これはつまり、問答無用で保持したいものを保持でき、それをそのままGoの世界に戻すことができるということです。この機能を実現するために、Goから直接ライブラリ関数を呼び出すのではなく、C言語のラッパーを作ります。
goGetFiles()
. このラッパーが、実際にGoのコールバックをuserdataオブジェクトと一緒に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 値の両方を保持するカスタム構造体に格納されます。これを
goGetFiles()
をuserdataパラメータとして指定します。
// 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(...)
関連
-
g++が内部・外部コマンドソリューションとして認識されない、MinGWを初めて使うときの落とし穴
-
#137: 式は変更可能なlvalueでなければならない問題 // 文字列配列の代入問題
-
VSCodeでCプログラムを書くとエラーになる:ソースファイル "stdio.h" を開くことができない
-
[解決済み] C言語でchar配列をコピーする方法は?
-
[解決済み] C言語で関数型プログラミングを行うためのツールにはどのようなものがありますか?
-
[解決済み] Cプリプロセッサはなぜ "linux "という単語を定数 "1 "と解釈するのですか?
-
[解決済み] Cコードの単体テスト【終了しました
-
[解決済み] なぜsizeof(x++)はxをインクリメントしないのですか?
-
[解決済み] C言語のi++と++iに性能差はあるのでしょうか?
-
[解決済み] C 言語の配列へのポインタ/ポインタの配列の曖昧さ解消
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
#137: 式は変更可能なlvalueでなければならない問題 // 文字列配列の代入問題
-
[解決済み] Code::Blocks アプリケーションをコンパイルできない
-
[解決済み] PTHREAD_MUTEX_INITIALIZER vs pthread_mutex_init ( &mutex, param )
-
[解決済み] C言語でchar配列をコピーする方法は?
-
[解決済み] stdinとSTDIN_FILENOの違いは何ですか?
-
[解決済み] C言語の書式指定子 %ul と %lu の違いは何ですか?
-
[解決済み] Linux Socket write() によるBad File Descriptor C
-
[解決済み] char *とchar[]の違い [重複]
-
[解決済み] C言語でランダムなint型を生成するには?
-
[解決済み] C 言語の配列へのポインタ/ポインタの配列の曖昧さ解消