関連記事
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 *)へ変換) - いろいろ備忘録日記
Goメモ-516 (cgoメモ-11)([]byteを(char *)へ変換) - いろいろ備忘録日記
Goメモ-518 (cgoメモ-12)(Cのmallocをcgo経由で呼び出し) - いろいろ備忘録日記
Goメモ-519 (cgoメモ-13)(ポインタ演算) - いろいろ備忘録日記
Goメモ-520 (cgoメモ-14)(Goの関数をCの世界に公開 (export)) - いろいろ備忘録日記
Goメモ-522 (cgoメモ-15)(Goでsoファイルを作成してC言語から呼び出し) - いろいろ備忘録日記
Goメモ-525 (cgoメモ-16)(C側にて関数ポインタを引数に要求する関数にGo側で定義した関数を設定) - いろいろ備忘録日記
Goメモ-526 (cgoメモ-17)(cgoとdlopen関数を使って既存ライブラリの呼び出しをフックする) - いろいろ備忘録日記
Goメモ-527 (cgoメモ-18)(cgoを利用している場合のinit関数について) - いろいろ備忘録日記
Goメモ-528 (cgoメモ-19)(cgoを利用して作成したsoファイル経由でのinit関数の呼び出し) - いろいろ備忘録日記
Goメモ-529 (cgoメモ-20)(C言語のNULLをcgoから渡す) - いろいろ備忘録日記
Goメモ-530 (cgoメモ-21)(CGOヘッダーで指定出来るCFLAGS, LDFLAGS) - いろいろ備忘録日記
Goメモ-531 (cgoメモ-22)(CGOで利用するコンパイラを変更する) - いろいろ備忘録日記
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。
今回から複数回に渡って cgo についてメモしていこうと思います。
cgo は、文字通りGoからCにアクセスすることが出来るようになるものなのですが、とても便利な反面、結構クセが強いのでメモでも残しておかないとすぐ頭から消えてしまいそうだなって思いました。
Cgo is not Go
という格言があったりするので、Go界隈で標準で推奨されていない技術かもしれません。が、実務ではC言語で作成されたライブラリなどは山のようにあります。んで、プロジェクトの方針でGoで作り直すことも出来ない場合も多々あります。そのような場合に非常に便利です。
これからのサンプルは以下のリポジトリにアップしてありますので、良ければご参考ください。
今回は cgo ヘッダで利用できる ${SRCDIR} 変数について。
cgoでは、cgoヘッダーのcgoディレクティブにて ${SRCDIR} という変数が利用できます。
/* #cgo LDFLAGS: -L${SRCDIR}/libs -llibs */ import "C"
#includeの部分には利用出来ません。つまり以下は駄目です。
/* // これはオッケイ #cgo LDFLAGS: -L${SRCDIR}/libs -llibs // 以下は駄目 #include "${SRCDIR}/libs/libs.h" */ import "C"
この場合は、以下のようにします。
/* #cgo CFLAGS: -I${SRCDIR}/libs #cgo LDFLAGS: -L${SRCDIR}/libs -llibs #include "libs.h" */ import "C"
この値は、実行時に現在のソースファイルのディレクトリに置換されます。ディレクトリパスは絶対パスで展開されます。
https://pkg.go.dev/cmd/cgo に以下の記載で記されています。
When the cgo directives are parsed, any occurrence of the string ${SRCDIR} will be replaced by the absolute path to the directory containing the source file.
(cgoディレクティブが解析されるとき、${SRCDIR}という文字列は、ソースファイルを含むディレクトリへの絶対パスに置き換えられます。)
サンプル
あらかじめ、ソースディレクトリの直下に libs というディレクトリを作り、その中にcgo経由で呼び出されるCの共有ライブラリがあるとします。ライブラリのヘッダーファイルもこのディレクトリにあります。
main.go
package main /* #cgo CFLAGS: -I${SRCDIR}/libs #cgo LDFLAGS: -L${SRCDIR}/libs -llibs #include "libs.h" */ import "C" import "log" func init() { log.SetFlags(0) } func main() { var ( x = C.int(10) y = C.int(20) ) log.Println(int(C.add(x, y))) }
Taskfile.yml
# https://taskfile.dev version: '3' tasks: default: cmds: - task: build-lib - task: build-goapp - task: run-goapp clean: cmds: - make -C libs clean - rm -f ./app build-lib: dir: libs cmds: - make build build-goapp: cmds: - go build -o app run-goapp: cmds: - ./app env: LD_LIBRARY_PATH: libs
実行
$ task task: [build-lib] make build gcc -g -O0 -Wall -Wextra -std=c17 -c /workspace/try-golang-cgo/22.C_SRCDIR/libs/libs.c -o /workspace/try-golang-cgo/22.C_SRCDIR/libs/libs.o gcc -g -O0 -Wall -Wextra -std=c17 -fPIC -shared -o ./liblibs.so /workspace/try-golang-cgo/22.C_SRCDIR/libs/libs.o task: [build-goapp] go build -o app task: [run-goapp] ./app 30
ちゃんとコンパイル出来て実行もできていますね。
参考情報
- C? Go? Cgo!
- Go Wiki: cgo
- cmd/cgo
- runtime/cgo
- cgoを使ったCとGoのリンクの裏側 (1)
- cgoを使ったCとGoのリンクの裏側 (2)
- ebitengine/purego
- JupiterRider/ffi
個人的Goのおすすめ書籍
個人的に読んでとても勉強になった書籍さんたちです。
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。