Goブログ
C? Go? Cgo!
はじめに
Cgoを使用すると、GoパッケージからCコードを呼び出すことができます。いくつかの特別な機能を備えたGoソースファイルが与えられた場合、cgoは単一のGoパッケージに結合できるGoファイルとCファイルを出力します。
例として、Cのrandom
関数とsrandom
関数をラップする2つの関数 - Random
とSeed
- を提供するGoパッケージを以下に示します。
package rand
/*
#include <stdlib.h>
*/
import "C"
func Random() int {
return int(C.random())
}
func Seed(i int) {
C.srandom(C.uint(i))
}
インポート文から始めて、ここで何が起こっているかを見てみましょう。
rand
パッケージは"C"
をインポートしますが、標準のGoライブラリにはそのようなパッケージはありません。これは、C
が「擬似パッケージ」、つまりcgoによってCの名前空間への参照として解釈される特別な名前であるためです。
rand
パッケージには、C
パッケージへの4つの参照が含まれています。C.random
とC.srandom
の呼び出し、変換C.uint(i)
、およびimport
文です。
Random
関数は、標準のCライブラリのrandom
関数を呼び出し、結果を返します。Cでは、random
はC型のlong
の値を返します。これは、cgoが型C.long
として表します。このパッケージ外のGoコードで使用する前に、通常のGo型変換を使用してGo型に変換する必要があります。
func Random() int {
return int(C.random())
}
型変換をより明確に示すために一時変数を使用する同等の関数を次に示します。
func Random() int {
var r C.long = C.random()
return int(r)
}
Seed
関数は、ある意味で逆を行います。これは、通常のGoのint
を受け取り、それをCのunsigned int
型に変換し、Cの関数srandom
に渡します。
func Seed(i int) {
C.srandom(C.uint(i))
}
cgoはunsigned int
型をC.uint
として認識していることに注意してください。これらの数値型名の完全なリストについては、cgoドキュメントを参照してください。
この例でまだ検討していない1つの詳細は、import
文の上にあるコメントです。
/*
#include <stdlib.h>
*/
import "C"
Cgoはこのコメントを認識します。スペース文字が続く#cgo
で始まる行は削除されます。これらはcgoのディレクティブになります。残りの行は、パッケージのC部分をコンパイルするときにヘッダーとして使用されます。この場合、これらの行は単一の#include
文にすぎませんが、ほぼすべてのCコードにすることができます。#cgo
ディレクティブは、パッケージのC部分を構築するときにコンパイラーとリンカーにフラグを提供するために使用されます。
制限事項があります。プログラムが//export
ディレクティブを使用する場合、コメント内のCコードには、定義(int f() { return 1; }
)ではなく、宣言(extern int f();
)のみを含めることができます。//export
ディレクティブを使用して、Go関数をCコードからアクセスできるようにすることができます。
#cgo
および//export
ディレクティブについては、cgoドキュメントで説明されています。
文字列など
Goとは異なり、Cには明示的な文字列型はありません。Cの文字列は、ヌル終端のcharの配列で表されます。
GoとCの文字列間の変換は、C.CString
、C.GoString
、およびC.GoStringN
関数を使用して行われます。これらの変換では、文字列データのコピーが作成されます。
次の例は、stdio
ライブラリのCのfputs
関数を使用して文字列を標準出力に書き込むPrint
関数を実装しています。
package print
// #include <stdio.h>
// #include <stdlib.h>
import "C"
import "unsafe"
func Print(s string) {
cs := C.CString(s)
C.fputs(cs, (*C.FILE)(C.stdout))
C.free(unsafe.Pointer(cs))
}
Cコードによって行われたメモリ割り当ては、Goのメモリマネージャーには認識されません。C.CString
(または任意のCメモリ割り当て)でC文字列を作成する場合は、C.free
を呼び出して、使い終わったらメモリを解放することを忘れないでください。
C.CString
の呼び出しは、char配列の先頭へのポインターを返すため、関数が終了する前に、unsafe.Pointer
に変換し、C.free
でメモリ割り当てを解放します。cgoプログラムの一般的なイディオムは、defer
割り当て直後に解放することです(特に、後続のコードが単一の関数呼び出しよりも複雑な場合)。次に、Print
の書き換えで示します。
func Print(s string) {
cs := C.CString(s)
defer C.free(unsafe.Pointer(cs))
C.fputs(cs, (*C.FILE)(C.stdout))
}
cgoパッケージの構築
cgoパッケージを構築するには、通常どおりgo build
またはgo install
を使用するだけです。goツールは、特別な"C"
インポートを認識し、これらのファイルに自動的にcgoを使用します。
cgoに関するその他のリソース
cgoコマンドドキュメントには、C擬似パッケージとビルドプロセスに関する詳細が記載されています。Goツリーのcgoの例では、より高度な概念を示しています。
最後に、これがすべて内部でどのように機能するかに興味がある場合は、runtimeパッケージのcgocall.goの紹介コメントをご覧ください。
次の記事:Gobs of data
前の記事:Goがより安定化
ブログインデックス