関連記事
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)(ポインタ演算) - いろいろ備忘録日記
GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ
概要
以下、自分用のメモです。
今回から複数回に渡って cgo についてメモしていこうと思います。
cgo は、文字通りGoからCにアクセスすることが出来るようになるものなのですが、とても便利な反面、結構クセが強いのでメモでも残しておかないとすぐ頭から消えてしまいそうだなって思いました。
Cgo is not Go
という格言があったりするので、Go界隈で標準で推奨されていない技術かもしれません。が、実務ではC言語で作成されたライブラリなどは山のようにあります。んで、プロジェクトの方針でGoで作り直すことも出来ない場合も多々あります。そのような場合に非常に便利です。
これからのサンプルは以下のリポジトリにアップしてありますので、良ければご参考ください。
今回は Go側の関数をCの世界に公開 (export) について。
cgoでは、Goの関数直上のコメントにて
//export function-name
とすると、この関数をC側にエクスポートすることが出来ます。つまり、C言語の方からGoで定義された関数を呼べるようになります。
今回のサンプルは、少しファイル数も多くなっていますので、サンプルの概要は以下のREADME.mdを見て頂いたほうが早いです。
try-golang-cgo/14.C_ExportGoFunc
サンプル
sample.h
#pragma once extern void go_start(); extern void go_end(); extern void go_main(int id, void *data, size_t length);
sub.go
package main /* #include <stdio.h> #include <stdlib.h> #include "sample.h" */ import "C" import ( "log" "unsafe" ) //export go_start func go_start() { log.Println("[Go][go_start] start") } //export go_end func go_end() { log.Println("[Go][go_end ] end") } //export go_main func go_main(cId C.int, cData unsafe.Pointer, cLength C.size_t) { var ( id = int(cId) length = int(cLength) data = C.GoStringN((*C.char)(cData), C.int(length)) ) log.Println("[Go][go_main ] called") log.Printf("[Go][go_main ] id=%d, data=%q, length=%d\n", id, data, length) }
main.go
package main /* #include <stdio.h> #include <stdlib.h> #include <string.h> #include "sample.h" void c_run() { go_start(); int id = 100; char data[] = "hello world"; size_t szData = strnlen(data, 20); go_main(id, data, szData); go_end(); } */ import "C" func main() { C.c_run() }
Taskfile.yml
# https://taskfile.dev version: '3' tasks: default: cmds: - go run . build: cmds: - go build -o app - nm ./app | grep -E "T (go_start|go_end|go_main)$"
実行
$ task task: [default] go run . 2024/12/12 02:30:56 [Go][go_start] start 2024/12/12 02:30:56 [Go][go_main ] called 2024/12/12 02:30:56 [Go][go_main ] id=100, data="hello world", length=11 2024/12/12 02:30:56 [Go][go_end ] end $ task build task: [build] go build -o app task: [build] nm ./app | grep -E "T (go_start|go_end|go_main)$" 000000000049b6d0 T go_end 000000000049b740 T go_main 000000000049b660 T go_start
参考情報
- C? Go? Cgo!
- Go Wiki: cgo
- cmd/cgo
- runtime/cgo
- cgoを使ったCとGoのリンクの裏側 (1)
- cgoを使ったCとGoのリンクの裏側 (2)
- ebitengine/purego
- JupiterRider/ffi
Goのおすすめ書籍
過去の記事については、以下のページからご参照下さい。
サンプルコードは、以下の場所で公開しています。