はじめに

CgoはGoパッケージがCコードを呼び出すことを可能にします。特別な機能を持つGoソースファイルが与えられると、cgoはGoとCのファイルを出力し、それらを単一のGoパッケージに結合できます。

例を挙げると、Cのrandomおよびsrandom関数をラップする2つの関数 - RandomおよびSeedを提供するGoパッケージがあります。

  1. package rand
  2. /*
  3. #include <stdlib.h>
  4. */
  5. import "C"
  6. func Random() int {
  7. return int(C.random())
  8. }
  9. func Seed(i int) {
  10. C.srandom(C.uint(i))
  11. }

ここで何が起こっているのかを見てみましょう。まずはインポート文から始めます。

  1. `````rand`````パッケージには、`````C`````パッケージへの4つの参照が含まれています:`````C.random`````および`````C.srandom`````への呼び出し、`````C.uint(i)`````への変換、そして`````import`````文です。
  2. `````Random`````関数は標準Cライブラリの`````random`````関数を呼び出し、その結果を返します。Cでは、`````random`````C`````long`````の値を返し、cgoはこれを`````C.long`````型として表現します。これは、このパッケージの外部でGoコードによって使用される前に、通常のGo型変換を使用してGo型に変換する必要があります:
  3. ``````bash
  4. func Random() int {
  5. return int(C.random())
  6. }
  7. `

ここに、型変換をより明示的に示すために一時変数を使用する同等の関数があります:

  1. func Random() int {
  2. var r C.long = C.random()
  3. return int(r)
  4. }

Seed関数は逆のことを行います。これは、通常のGo intを受け取り、それをC unsigned int型に変換し、C関数srandomに渡します。

  1. func Seed(i int) {
  2. C.srandom(C.uint(i))
  3. }

cgoはunsigned int型をC.uintとして認識します。これらの数値型名の完全なリストについては、cgoドキュメントを参照してください。

この例の1つの詳細は、import文の上にあるコメントです。

  1. /*
  2. #include <stdlib.h>
  3. */
  4. import "C"

Cgoはこのコメントを認識します。#cgoで始まり、スペース文字が続く行は削除されます。これらはcgoの指示になります。残りの行は、パッケージのC部分をコンパイルする際のヘッダーとして使用されます。この場合、これらの行は単一の#include文ですが、ほぼ任意のCコードにすることができます。#cgo指示は、パッケージのC部分をビルドする際にコンパイラとリンカにフラグを提供するために使用されます。

制限があります:プログラムが//export指示を使用する場合、コメント内のCコードには宣言(extern int f();)のみが含まれ、定義(int f() { return 1; })は含まれてはいけません。//export指示を使用して、Go関数をCコードにアクセス可能にすることができます。

  1. <a name="strings-and-things"></a>
  2. ## 文字列とその他のもの
  3. Goとは異なり、Cには明示的な文字列型がありません。Cの文字列は、ゼロ終端の文字配列で表されます。
  4. GoとCの文字列間の変換は、`````C.CString`````、`````C.GoString`````、および`````C.GoStringN`````関数を使用して行われます。これらの変換は文字列データのコピーを作成します。
  5. 次の例は、Cの`````fputs`````関数を使用して標準出力に文字列を書き込む`````Print`````関数を実装しています:
  6. ``````bash
  7. package print
  8. // #include <stdio.h>
  9. // #include <stdlib.h>
  10. import "C"
  11. import "unsafe"
  12. func Print(s string) {
  13. cs := C.CString(s)
  14. C.fputs(cs, (*C.FILE)(C.stdout))
  15. C.free(unsafe.Pointer(cs))
  16. }
  17. `

Cコードによって行われたメモリ割り当ては、Goのメモリマネージャーには知られていません。C.CString(または任意のCメモリ割り当て)を使用してC文字列を作成する場合、C.freeを呼び出して処理が完了したときにメモリを解放することを忘れないでください。

  1. ``````bash
  2. func Print(s string) {
  3. cs := C.CString(s)
  4. defer C.free(unsafe.Pointer(cs))
  5. C.fputs(cs, (*C.FILE)(C.stdout))
  6. }
  7. `

cgoパッケージのビルド

cgoパッケージをビルドするには、通常通りにgo buildまたはgo installを使用します。goツールは特別な"C"インポートを認識し、それらのファイルに対して自動的にcgoを使用します。

cgoリソースの詳細

cgoコマンドのドキュメントには、C擬似パッケージとビルドプロセスに関する詳細が記載されています。Goツリーのcgo例は、より高度な概念を示しています。

最後に、これが内部でどのように機能するのかに興味がある場合は、ランタイムパッケージのcgocall.goの導入コメントを見てください。