cmd/cgoの翻訳

pkg.go.dev

go1.17時点

Goの参照からC

Go ファイル内では、Go のキーワードである C の構造体のフィールド名にアンダースコアを前置することでアクセスできます。x が type という名前のフィールドを持つ C 構造体を指している場合、x._type はそのフィールドにアクセスします。ビットフィールドやミスアラインドデータなど、Go で表現できない C 構造体のフィールドは、Go 構造体では省略され、次のフィールドや構造体の最後に到達するための適切なパディングで置き換えられます。

C.char、C.schar(符号付き char)、C.uchar(符号なし char)、C.short、C.ushort(符号なし short)、C.int、C.uint(符号なし int)、C.long、C.ulong(符号なし long)、C.longlong(long)、C.ulonglong(符号なし long long)、C.float、C.double、C.complexfloat(複素数 float)、C.complexdouble(複素数 double)という名前で、標準的な C の数値型が用意されています。C型のvoid*は、Goのunsafe.Pointerで表されます。C型のint128_tおよびuint128_tは、[16]byte.Pointerで表されます。

いくつかの特殊なC言語の型は、通常、Goではポインタ型で表現されますが、代わりにuintptrで表現されます。以下の「特殊なケース」を参照してください。

構造体(struct)、組合(union)、列挙型(enum)に直接アクセスするには、C.struct_statのように、構造体(struct)、組合(union)、列挙型(enum_)を前置します。

任意のC型Tのサイズは、C.sizeof_struct_statのように、C.sizeof_Tとして利用できます。

C関数は、特別な名前GoStringのパラメータ型を持つGoファイルで宣言することができます。この関数は、通常の Go の文字列値で呼び出すことができます。文字列の長さと文字列の内容へのポインタにアクセスするには、以下のC関数を呼び出す必要があります。

size_t _GoStringLen(_GoString_ s);
const char *_GoStringPtr(_GoString_ s);

これらの関数はプリアンブルでのみ使用でき、他の C ファイルでは使用できません。C コードは、_GoStringPtr が返すポインタの内容を変更してはいけません。文字列の内容は、末尾に NUL バイトがあってはならないことに注意してください。

Go は一般的なケースでは C の union 型をサポートしていないので、C の union 型は同じ長さの Go のバイト配列として表されます。

Goの構造体は、Cの型を持つフィールドを埋め込むことができません。

Goのコードは、空ではないCの構造体の最後に発生するゼロサイズのフィールドを参照できません。このようなフィールドのアドレスを取得するには(ゼロサイズのフィールドでできる唯一の操作です)、構造体のアドレスを取り、構造体のサイズを加える必要があります。

Cgo は C 言語の型を同等の非移植の Go 言語の型に変換します。ある Go パッケージで使用されている C 型は、別の Go パッケージで使用されている同じ C 型とは異なります。

任意の C 関数 (void 関数も含む) を多重代入コンテキストで呼び出して、戻り値 (もしあれば) とエラーとしての C errno 変数の両方を取得することができます (関数が void を返す場合は _ を使用して結果値をスキップします)。例えば、以下のようになります。

n, err = C.sqrt(-1)
_, err := C.voidFunc()
var n, err = C.sqrt(1)

現在、C言語の関数ポインタの呼び出しはサポートされていませんが、C言語の関数ポインタを保持するGo変数を宣言し、GoとCの間でそれらをやり取りすることができます。Cコードは、Goから受け取った関数ポインタを呼び出すことができます。例えば、以下のようになります。

package main

// typedef int (*intFunc) ();
//
// int
// bridge_int_func(intFunc f)
// {
//     return f();
// }
//
// int fortytwo()
// {
//     return 42;
// }
import "C"
import "fmt"

func main() {
    f := C.intFunc(C.fortytwo)
    fmt.Println(int(C.bridge_int_func(f)))
    // Output: 42
}

Cでは、固定サイズの配列として書かれた関数の引数は、実際には配列の最初の要素へのポインタを必要とします。C言語コンパイラはこの呼び出し規則を認識しており、それに応じて呼び出しを調整しますが、Goではそれができません。Goでは、最初の要素へのポインタを明示的に渡さなければなりません。C.f(&C.x[0]).

可変長のC関数を呼び出すことはサポートされていません。C関数のラッパーを使用することで、この問題を回避することができます。例えば,以下のようになります.

package main

// #include <stdio.h>
// #include <stdlib.h>
//
// static void myprint(char* s) {
//   printf("%s\n", s);
// }
import "C"
import "unsafe"

func main() {
    cs := C.CString("Hello from stdio")
    C.myprint(cs)
    C.free(unsafe.Pointer(cs))
}

いくつかの特殊な関数は、データのコピーを作成することで、Go型とC型の間で変換します。擬似的なGoの定義で。

// Go stringからC stringへ
// Cの文字列は、mallocを使ってCのヒープに割り当てられます。
// C.free(C.freeが必要な場合はstdlib.hを必ずインクルードしてください)を呼び出すなどして、
// 解放されるように手配するのは呼び出し側の責任です。
func C.CString(string) *C.char

// Cの配列は、mallocを使ってCのヒープに割り当てられます。
// C.free(C.freeが必要な場合はstdlib.hを必ずインクルードしてください)を呼び出すなど、
// 呼び出し側の責任で解放されるように手配します。
func C.CBytes([]byte) unsafe.Pointer

// C string から Go stringへ
func C.GoString(*C.char) string

// 明示的な長さを持つC data からGo stringへ
func C.GoStringN(*C.char, C.int) string

// 明示的な長さを持つC data から Go []byteへ
func C.GoBytes(unsafe.Pointer, C.int) []byte

特殊なケースとして、C.mallocはCライブラリのmallocを直接呼び出すのではなく、Cライブラリのmallocをラップして、決してnilを返さないことを保証するGoのヘルパー関数を呼び出します。Cのmallocがメモリ不足を示した場合、Go自身がメモリ不足に陥ったときのように、ヘルパー関数がプログラムをクラッシュさせます。C.mallocは失敗することができないので、errnoを返す2つの結果の形式はありません。

Cの参照からGo

Go関数は、次のようにしてCコードで使用できるようにエクスポートできます。

//export MyFunction
func MyFunction(arg1, arg2 int, arg3 string) int64 {...}

//export MyFunction2
func MyFunction2(arg1, arg2 int, arg3 string) (int64, *C.char) {...}

これらはCコードとして以下のように利用できます。

extern GoInt64 MyFunction(int arg1, int arg2, GoString arg3);
extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3);

cgo_export.hの生成されたヘッダの中で、cgoの入力ファイルからコピーされたプリアンブルの後に記述されています。複数の戻り値を持つ関数は、構造体を返す関数にマッピングされます。

すべての Go の型が C の型に便利な方法でマッピングできるわけではありません。Go の構造体タイプはサポートされていないので、C の構造体タイプを使用してください。Go の配列型はサポートされていません。C のポインタを使用してください。

文字列型の引数を取るGo関数は、上述のCのGoString型で呼び出すことができます。このGoString型は、プリアンブルで自動的に定義されます。これは、文字列の値をGoからCに渡したり、Goに戻したりする場合にのみ有効です。

ファイル内で //export を使用すると、プリアンブルに制限がかかります。プリアンブルは 2 つの異なる C 出力ファイルにコピーされるため、定義を含んではならず、宣言のみが含まれます。ファイルに定義と宣言の両方が含まれていると、2つの出力ファイルでシンボルが重複してしまい、リンカが失敗してしまいます。これを避けるためには、他のファイルのプリアンブルやCソースファイルの中に定義を入れる必要があります。

Passing pointers

Goはガベージコレクション言語であり、ガベージコレクタはGoのメモリへのすべてのポインタの位置を知る必要があります。そのため、GoとCの間でのポインタの受け渡しには制限があります。

本節では、Goポインタとは、Goによって割り当てられたメモリへのポインタ(&演算子の使用や定義済みのnew関数の呼び出しなど)を意味し、Cポインタとは、Cによって割り当てられたメモリへのポインタ(C.mallocの呼び出しなど)を意味します。ポインターがGoポインターであるかCポインターであるかは、メモリがどのように割り当てられたかによって決まる動的な性質であり、ポインターの型とは関係ありません。

いくつかのGo型の値は、その型のゼロ値以外は、常にGoポインタを含んでいることに注意してください。これは、string、slice、interface、channel、map、functionの各型に当てはまります。ポインタ型は、Goポインタを保持していても、Cポインタを保持していてもかまいません。配列型と構造体型は、要素の型によって、Goポインタを含む場合と含まない場合があります。以下のGoポインターに関する説明は、ポインター型だけでなく、Goポインターを含む他の型にも適用されます。

Go コードは、Go ポインタが指す Go メモリに Go ポインタが含まれていない場合、C に Go ポインタを渡すことができます。Cコードはこの特性を維持しなければなりません。たとえ一時的であっても、GoポインタをGoメモリに格納してはなりません。構造体のフィールドへのポインタを渡す場合、問題となるGoメモリは構造体全体ではなく、フィールドが占有するメモリです。配列やスライスの要素へのポインタを渡す場合、問題となるGoメモリは、配列全体またはスライスの下位配列全体です。

Cコードは、呼び出しが戻った後に、Goポインタのコピーを保持することはできません。これには GoString 型も含まれます。上述のように、GoString 型は Go ポインタを含んでいます。

Cコードから呼び出されたGo関数は、Goポインタを返すことはできません(これは、文字列、スライス、チャンネルなどを返すことができないことを意味します)。Cコードによって呼び出されたGo関数は、引数としてCポインタを取ることができ、それらのポインタを通して非ポインタまたはCポインタのデータを格納することができますが、Cポインタによって指されるメモリにGoポインタを格納することはできません。C コードから呼び出される Go 関数は、引数として Go ポインタを取ることができますが、その関数が指す Go メモリには Go ポインタが含まれていないという特性を保持する必要があります。

Go コードは、C メモリに Go ポインタを格納することはできませんCコードはCメモリにGoポインタを格納することができますが、上記のルールに従い、C関数が戻るときにGoポインタの格納を停止しなければなりません。

これらの規則は、実行時に動的にチェックされます。このチェックは、環境変数 GODEBUG の cgocheck の設定によって制御されます。デフォルトの設定は GODEBUG=cgocheck=1 で、これは合理的に安価な動的チェックを実装しています。GODEBUG=cgocheck=0 を使用すると、これらのチェックを完全に無効にすることができます。 GODEBUG=cgocheck=2 を使用すると、実行時に多少のコストがかかりますが、ポインタ処理の完全なチェックを行うことができます。

unsafeパッケージを使用することで、この強制力を無効にすることは可能ですし、もちろんCコードが好きなことをするのを止めることはできません。しかし、これらのルールを破ったプログラムは、予期せぬ予期せぬ方法で失敗する可能性があります。

runtime/cgo.Handle型は、GoとCの間でGoの値を安全に渡すために使用できます。詳細はruntime/cgoパッケージのドキュメントを参照してください。

注意: 現在の実装にはバグがあります。GoコードはCメモリにnilやCポインタ(Goポインタではない)を書き込むことが許可されていますが、現在の実装では、Cメモリの内容がGoポインタであるように見える場合、ランタイムエラーが発生することがあります。そのため、Goコードがポインタ値を格納する場合は、初期化されていないCメモリをGoコードに渡さないようにしてください。Goに渡す前にCでメモリをゼロにしてください。

Special cases

いくつかの特殊なC言語の型は、通常、Goではポインタ型で表現されますが、代わりにuintptrで表現されます。それらは以下の通りです。

  1. Darwinの*Ref型は、CoreFoundationのCFTypeRef型に根ざしています。

  2. JavaのJNIインターフェースのオブジェクト型。

jobject
jclass
jthrowable
jstring
jarray
jbooleanArray
jbyteArray
jcharArray
jshortArray
jintArray
jlongArray
jfloatArray
jdoubleArray
jobjectArray
jweak
  1. EGL API の EGLDisplay および EGLConfig 型です。

これらの型は、そうしないとGoのガベージコレクタを混乱させるため、Go側ではuintptrとなっています。これらは、実際にはポインタではなく、ポインタ型でエンコードされたデータ構造であることもあります。このような空の参照を初期化するための適切な定数はnilではなく0です。

これらの特殊なケースは、Go 1.10で導入されました。Go 1.9 以前のコードを自動更新するには、Go fix tool の cftype または jni rewrites を使用してください。

go tool fix -r cftype <pkg>
go tool fix -r jni <pkg>

適切な場所で nil を 0 に置き換えます。

EGLDisplayケースは、Go 1.12で導入されました。Go 1.11 以前のコードを自動更新するには、egl rewrite を使用してください。

go tool fix -r egl <pkg>

EGLConfigケースは、Go 1.15で導入されました。Go 1.14 以前のコードを自動更新するには、eglconf rewrite を使用してください。

go tool fix -r eglconf <pkg>

cgoを直接使用する

使い方:

go tool cgo [cgo options] [-- compiler options] gofiles...

Cgo は、指定された入力 Go ソースファイルを、複数の出力 Go および C ソースファイルに変換します。

C コンパイラオプションは、パッケージの C パートをコンパイルするために C コンパイラを起動する際に解釈されずに渡されます。

cgo を直接実行する場合、以下のオプションが利用できます。

-V
    Print cgo version and exit.
-debug-define
    Debugging option. Print #defines.
-debug-gcc
    Debugging option. Trace C compiler execution and output.
-dynimport file
    Write list of symbols imported by file. Write to
    -dynout argument or to standard output. Used by go
    build when building a cgo package.
-dynlinker
    Write dynamic linker as part of -dynimport output.
-dynout file
    Write -dynimport output to file.
-dynpackage package
    Set Go package for -dynimport output.
-exportheader file
    If there are any exported functions, write the
    generated export declarations to file.
    C code can #include this to see the declarations.
-importpath string
    The import path for the Go package. Optional; used for
    nicer comments in the generated files.
-import_runtime_cgo
    If set (which it is by default) import runtime/cgo in
    generated output.
-import_syscall
    If set (which it is by default) import syscall in
    generated output.
-gccgo
    Generate output for the gccgo compiler rather than the
    gc compiler.
-gccgoprefix prefix
    The -fgo-prefix option to be used with gccgo.
-gccgopkgpath path
    The -fgo-pkgpath option to be used with gccgo.
-godefs
    Write out input file in Go syntax replacing C package
    names with real values. Used to generate files in the
    syscall package when bootstrapping a new target.
-objdir directory
    Put all generated files in directory.
-srcdir directory