31.C_callback

command
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 7, 2025 License: MIT Imports: 3 Imported by: 0

README

cgo callback サンプル

概要

本サンプルは、cgoを利用してCの関数からGoの関数をコールバックする方法を示します。

Goの main 関数からCで実装された c_func を呼び出します。このとき、Goで実装されエクスポートされた export_func の関数ポインタを引数として渡します。

C側の c_func は、受け取った関数ポインタを利用してGoの export_func を呼び出し、その結果を受け取ってGoの main 関数に返します。

処理の流れ

  1. Goの main 関数が開始されます。
  2. Goの main 関数は、Cの関数 c_func を呼び出します。引数として、整数 x, y と、Goの関数 export_func への関数ポインタを渡します。
  3. Cの c_func は、受け取った関数ポインタ fn を使って、Goの関数 export_func(x, y) を呼び出します(コールバック)。
  4. Goの export_func が実行され、xy の乗算結果を計算し、Cの c_func に返します。
  5. Cの c_func は、 export_func から受け取った値を、呼び出し元であるGoの main 関数に返します。
  6. Goの main 関数は、最終的な結果を受け取り、標準出力に表示します。

実行方法

go run .

実行結果

[C ] x=2, y=3
[Go] sleep 1sec
[Go] x=2, y=3, ans=6
[C ] ans=6
[Go] z=6

Goの関数ポインタの取得について

main.go の中で、Cの関数 c_func に渡すために export_func の関数ポインタを取得している以下のコードは、cgo特有のテクニックです。

fn := (*[0]byte)(C.export_func)

Cのコードでは、c_func の第3引数 fnint (*fn)(int, int) という関数ポインタ型として定義されています。 このため、Go側から c_func を呼び出す際には、Goの関数 export_func のメモリアドレス(ポインタ)を渡す必要があります。

しかし、cgoの仕様上、C.export_func という記述は、Goの世界では関数そのものを表しますが、直接ポインタを指すものではありません。そこで、一度 unsafe.Pointer を経由して型変換を行う必要がありますが、より一般的に使われるイディオムが (*[0]byte)(C.export_func) というキャストです。

これは、export_func を「長さ0のbyte配列へのポインタ」に変換することを意味します。Goのコンパイラは、このキャストを特別に解釈し、export_func の先頭アドレスを指すポインタを生成します。これにより、C側が期待する関数ポインタとして正しく渡すことができます。

補足: エクスポートしていないGo関数を渡す場合

//exportディレクティブを付けていないGoの関数(プライベートな関数やメソッドなど)を、Cの関数ポインタとして直接渡すことはできません。Cから見えるシンボルとして公開されていないためです。

このようなケースでは、cgo.Handle という仕組みを使って間接的に実現するのが一般的な方法です。これは、Goの値をC側で安全に参照するための「ハンドル」という整数値に変換する機能です。

手順の概要は以下の通りです。

  1. Go: cgo.NewHandle() を使い、コールバックさせたいGoの関数からハンドル(整数)を作成します。
  2. Go: Cの関数を呼び出す際に、このハンドルを void* などにキャストして渡します。
  3. C: Cの関数は、Goの関数ポインタの代わりにこのハンドルを受け取ります。
  4. C: コールバックが必要な場面で、仲介役となる エクスポート済みのGo関数(トランポリン関数) を呼び出し、引数としてハンドルを渡します。
  5. Go (トランポリン関数): 渡されたハンドルを handle.Value() で元のGoの関数に戻し、実行します。
  6. Go: ハンドルが不要になったら handle.Delete() で解放します。

この方法は少し複雑になりますが、Goのガベージコレクタに管理されているメモリ空間にある値をCの世界に安全に公開するための定石です。

このプロジェクトの 24.C_Using_cgo_Handle/ ディレクトリに、この cgo.Handle を利用したサンプルコードがありますので、参考にしてください。

Documentation

The Go Gopher

There is no documentation for this package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL