関連記事
Goメモ-502 (cgoメモ-01)(cgoヘッダ) - いろいろ備忘録日記
Goメモ-506 (cgoメモ-02)(cgoヘッダ) - いろいろ備忘録日記
Goメモ-507 (cgoメモ-03)(C.int) - いろいろ備忘録日記
Goメモ-508 (cgoメモ-04)(C言語の構造体) - いろいろ備忘録日記
Goメモ-509 (cgoメモ-05)(C.CString)(Cの文字列) - いろいろ備忘録日記
Goメモ-510 (cgoメモ-06)(C.GoString)(Cの文字列をGoの文字列へ) - いろいろ備忘録日記
Goメモ-511 (cgoメモ-07)(C.CBytes)([]byteをCのバイト列に) - いろいろ備忘録日記
Goメモ-512 (cgoメモ-08)(C.GoBytes)(Cのバイト列をGoの[]byteへ) - いろいろ備忘録日記
Goメモ-514 (cgoメモ-09)(C.GoStringN)(C.GoStringのサイズ指定版) - いろいろ備忘録日記
Goメモ-515 (cgoメモ-10)([]byteを(void *)へ変換) - いろいろ備忘録日記
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。
今回から複数回に渡って cgo についてメモしていこうと思います。
cgo は、文字通りGoからCにアクセスすることが出来るようになるものなのですが、とても便利な反面、結構クセが強いのでメモでも残しておかないとすぐ頭から消えてしまいそうだなって思いました。
Cgo is not Go
という格言があったりするので、Go界隈で標準で推奨されていない技術かもしれません。が、実務ではC言語で作成されたライブラリなどは山のようにあります。んで、プロジェクトの方針でGoで作り直すことも出来ない場合も多々あります。そのような場合に非常に便利です。
これからのサンプルは以下のリポジトリにアップしてありますので、良ければご参考ください。
今回は cgo で []byte
を (char *)
へ変換するやり方について。
前回、[]byte
を(void *)
へ変換するやり方について書いたのですが、それの応用ですね。
(char *)
も、頻繁に登場する子なので、Go側から変換して渡すことが多いです。
[]byte
を(char *)
に変換するには以下の手順を踏みます。
- スライスの先頭要素のポインタを取得
unsafe.Pointer
を取得- (*C.char)にキャスト
cgoでは、unsafe.Pointer()
がCの (void *)
に当たります。
Cの (void *)
と同様に、unsafe.Pointer
も任意のポインタ型にキャストできます。なので、任意の型のポインタが欲しい場合は一旦 unsafe.Pointer
にして、それから望みの型にキャストします。
C言語では式の中で使う配列は、「先頭要素のポインタ」に読み替えてもらえますので、C言語上では配列名をそのまま指定すれば良いのですが、GoはC言語では無いので当然それが出来ません。
なので、明示的に先頭要素のポインタを指定する必要があります。
サンプル
main.go
package main /* #include <stdio.h> #include <stdlib.h> #include <string.h> const int BUF_SIZE = 11; void setBuf(char *buf) { strncpy(buf, "helloworld", BUF_SIZE-1); } */ import "C" import ( "encoding/hex" "fmt" "unsafe" ) func main() { var ( buf = make([]byte, int(C.BUF_SIZE)) bufPtr = unsafe.Pointer(&buf[0]) //明示的に先頭要素のポインタを渡す charPtr = (*C.char)(bufPtr) // (void *) --> (char *) printBuf = func() { fmt.Println(string(buf)) fmt.Println(hex.Dump(buf)) } ) printBuf() { // C側の宣言では (char *) を引数に要求しているため、unsafe.Pointer を *C.char にキャストして渡す. // unsafe.Pointer は、 (void *) を表すので、任意のポインタ型にキャスト可能. C.setBuf(charPtr) } printBuf() }
Taskfile.yml
# https://taskfile.dev version: '3' tasks: default: cmds: - go run main.go
実行
$ task task: [default] go run main.go 00000000 00 00 00 00 00 00 00 00 00 00 00 |...........| helloworld 00000000 68 65 6c 6c 6f 77 6f 72 6c 64 00 |helloworld.|
ちゃんと (char *)
としてC側に渡って値を設定出来ていますね。
参考情報
- C? Go? Cgo!
- Go Wiki: cgo
- cmd/cgo
- runtime/cgo
- cgoを使ったCとGoのリンクの裏側 (1)
- cgoを使ったCとGoのリンクの裏側 (2)
- ebitengine/purego
- JupiterRider/ffi
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。