-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13 from devlights/add-call-go-func-from-c-via-fun…
…ction-pointer
- Loading branch information
Showing
6 changed files
with
103 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# C.CallGoFuncFromC_via_FunctionPointer | ||
|
||
[cgoのドキュメント](https://pkg.go.dev/cmd/cgo)には以下の記載があり、cgoではCの関数ポインタがサポートされていない。しかし、下記のようにやり取りする方法がある。 | ||
|
||
> Calling C function pointers is currently not supported, however you can declare Go variables which hold C function pointers and pass them back and forth between Go and C. C code may call function pointers received from Go. | ||
> Cの関数ポインターの呼び出しは現在サポートされていませんが、Cの関数ポインターを保持するGo変数を宣言し、GoとCの間で受け渡しすることができます。 | ||
C側にて関数ポインタを引数に要求する関数に、Go側で定義した関数を設定するのは手間がかかるが以下のようにする。 | ||
|
||
1. 関数ポインタのtypedefが存在しない場合はC側で定義する | ||
1. Goの関数をexportしてCの世界に公開する | ||
1. exportしたGoの関数をtypedefした定義でラップする | ||
1. ラップした値を引数に受取り、実際のCの関数を呼び出すラッパー関数を用意 | ||
|
||
詳細については、ソースコードを参照。 | ||
|
||
なお、注意点としてexportするGoの関数定義とcgoのプロトタイプ宣言を同じファイルでしてはいけない。(リンカーでエラーとなる) | ||
|
||
この点については、[cgoのドキュメント](https://pkg.go.dev/cmd/cgo)に以下の記載がある。 | ||
|
||
> Using //export in a file places a restriction on the preamble: since it is copied into two different C output files, it must not contain any definitions, only declarations. If a file contains both definitions and declarations, then the two output files will produce duplicate symbols and the linker will fail. To avoid this, definitions must be placed in preambles in other files, or in C source files. | ||
> ファイル内で//exportを使用すると、プリアンブルに制約が課される。プリアンブルは2つの異なるC出力ファイルにコピーされるため、定義が含まれてはならず、宣言のみが含まれる。ファイルに定義と宣言の両方が含まれていると、2つの出力ファイルに重複したシンボルが生成され、リンカは失敗する。これを避けるには、定義を他のファイルのプリアンブルか、Cのソース・ファイルに記述しなければならない。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# https://taskfile.dev | ||
|
||
version: '3' | ||
|
||
tasks: | ||
default: | ||
cmds: | ||
- go run . |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package main | ||
|
||
/* | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include "sample.h" | ||
// func_with_callback関数にて要求される関数ポインタを型定義 | ||
typedef void (*c_callback)(int, void *, size_t); | ||
// sub.go にて export しているGo側の関数のプロトタイプ | ||
// プロトタイプと実装を同じファイルで書き込んではいけない (リンカーにてエラーになる) | ||
void goCallback(int, void *, size_t); | ||
// cgo側より呼び出すラッパー関数 | ||
void go_func_with_callback(int id, c_callback cb) { | ||
func_with_callback(id, cb); | ||
} | ||
*/ | ||
import "C" | ||
|
||
func main() { | ||
// | ||
// cgo経由でコールバックを用意して呼び出し | ||
// | ||
var ( | ||
id = C.int(100) | ||
callback = C.c_callback(C.goCallback) | ||
) | ||
|
||
C.go_func_with_callback(id, callback) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include "sample.h" | ||
|
||
// 引数にコールバック用の関数ポインタを要求する関数 | ||
void func_with_callback(int id, void (*callback)(int id, void *data, size_t length)) { | ||
char data[] = "hello world"; | ||
size_t szData = strnlen(data, 12); | ||
|
||
printf("[C ][func_with_callback] called\n"); | ||
(*callback)(id, data, szData); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#ifndef SAMPLE_H | ||
#define SAMPLE_H | ||
|
||
void func_with_callback(int, void (*callback)(int, void *, size_t)); | ||
|
||
#endif /* SAMPLE_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package main | ||
|
||
import "C" | ||
import ( | ||
"fmt" | ||
"unsafe" | ||
) | ||
|
||
//export goCallback | ||
func goCallback(cId C.int, cData unsafe.Pointer, cLength C.size_t) { | ||
var ( | ||
id = int(cId) | ||
data = C.GoString((*C.char)(cData)) | ||
length = int(cLength) | ||
) | ||
|
||
fmt.Println("[Go][goCallback] called") | ||
fmt.Printf("[Go][goCallback] id=%d, data=%q, length=%d\n", id, data, length) | ||
} |