はじめに
CgoはGoパッケージがCコードを呼び出すことを可能にします。特別な機能を持つGoソースファイルが与えられると、cgoはGoとCのファイルを出力し、それらを単一のGoパッケージに結合できます。
例を挙げると、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`````パッケージへの4つの参照が含まれています:`````C.random`````および`````C.srandom`````への呼び出し、`````C.uint(i)`````への変換、そして`````import`````文です。
`````Random`````関数は標準Cライブラリの`````random`````関数を呼び出し、その結果を返します。Cでは、`````random`````はC型`````long`````の値を返し、cgoはこれを`````C.long`````型として表現します。これは、このパッケージの外部でGoコードによって使用される前に、通常のGo型変換を使用してGo型に変換する必要があります:
``````bash
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コードには宣言(extern int f();
)のみが含まれ、定義(int f() { return 1; }
)は含まれてはいけません。//export
指示を使用して、Go関数をCコードにアクセス可能にすることができます。
<a name="strings-and-things"></a>
## 文字列とその他のもの
Goとは異なり、Cには明示的な文字列型がありません。Cの文字列は、ゼロ終端の文字配列で表されます。
GoとCの文字列間の変換は、`````C.CString`````、`````C.GoString`````、および`````C.GoStringN`````関数を使用して行われます。これらの変換は文字列データのコピーを作成します。
次の例は、Cの`````fputs`````関数を使用して標準出力に文字列を書き込む`````Print`````関数を実装しています:
``````bash
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
を呼び出して処理が完了したときにメモリを解放することを忘れないでください。
``````bash
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例は、より高度な概念を示しています。
最後に、これが内部でどのように機能するのかに興味がある場合は、ランタイムパッケージのcgocall.goの導入コメントを見てください。