Pendahuluan

Cgo membolehkan paket Go memanggil kode C. Dengan sebuah sumber kode Go yang ditulis dengan fitur-fitur khusus, cgo menghasilkan berkas Go dan C yang dapat digabungkan menjadi sebuah paket Go.

Sebagai contohnya, berikut sebuah paket Go yang menyediakan dua fungsi — Random dan Seed — yang membungkus fungsi C random dan srandom.

package rand

/*
#include <stdlib.h>
*/
import "C"

func Random() int {
	return int(C.random())
}

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

Mari kita lihat apa yang terjadi di sini, dimulai dari perintah "import".

Paket rand meng-import "C", namun Anda tidak akan menemukan paket tersebut dalam pustaka standar Go. Hal ini karena "C" adalah "paket-pseudo", sebuah nama khusus yang diartikan oleh cgo sebagai referensi ke ruang nama C.

Paket rand berisi empat referensi ke paket C: pemanggilan ke C.random dan C.srandom, konversi C.uint(i), dan perintah import.

Fungsi Random memanggil fungsi random yang ada dalam pustaka standar C dan mengembalikan hasilnya. Dalam C, fungsi random mengembalikan sebuah nilai C bertipe long, yang direpresentasikan oleh cgo sebagai tipe C.long. Nilai tersebut harus dikonversi ke tipe Go sebelum dapat digunakan oleh kode Go lainnya, menggunakan konversi tipe Go biasa:

func Random() int {
	return int(C.random())
}

Berikut fungsi yang sama yang menggunakan variabel sementara untuk memperlihatkan konversi tipe lebih eksplisit:

func Random() int {
	var r C.long = C.random()
	return int(r)
}

Fungsi Seed melakukan hal yang sama, namun terbalik. Fungsi tersebut menerima Go int, mengonversinya ke tipe C unsigned int, dan mengirimnya ke fungsi C srandom.

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

Perlu diketahui bahwa cgo mengenal tipe unsigned int sebagai C.uint; lihat dokumentasi cgo untuk daftar komplit dari nama tipe numerik pada cgo.

Salah satu perintah dari contoh di atas yang belum kita jelaskan yaitu komentar di atas perintah "import".

/*
#include <stdlib.h>
*/
import "C"

Cgo mengenali komentar tersebut. Setiap baris komentar yang dimulai dengan #cgo diikuti oleh karakter spasi akan dihapus; baris tersebut akan menjadi direktif untuk cgo. Sisa baris berikutnya digunakan sebagai header saat mengompilasi bagian C dari paket. Pada contoh ini, baris-baris tersebut adalah sebuah perintah "#include", namun ia bisa saja berupa kode C apa pun. Direktif #cgo digunakan untuk mengirim opsi bagi compiler dan linker saat membangun bagian C dari paket.

Aturan tersebut ada batasannya: jika program Anda menggunakan direktif "//export", maka kode C dalam komentar hanya boleh mengikutkan deklarasi (misalnya, extern int f();), bukan definisi (misalnya, int f() { return 1; }). Anda bisa menggunakan direktif "//export" untuk membuat fungsi Go dapat diakses oleh kode C.

Direktif "#cgo" dan "//export" di-dokumentasikan dalam dokumentasi cgo.

Hal-hal yang berkaitan dengan string

Tidak seperti Go, C tidak memiliki tipe string. String dalam C direpresentasikan oleh array dari char yang diakhiri oleh 0.

Konversi string antara Go dan C dilakukan lewat fungsi C.CString, C.GoString, dan C.GoStringN. Konversi-konversi tersebut membuat salinan dari data string.

Contoh berikut ini mengimplementasikan fungsi Print yang menulis sebuah string ke standar keluaran menggunakan fungsi C fputs dari pustaka stdio:

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))
}

Alokasi memori yang dilakukan oleh C tidak diketahui oleh manajer memori pada Go. Saat Anda membuat sebuah C string dengan C.CString (atau alokasi memori apa pun pada C), jangan sampai lupa untuk melepaskan kembali memori tersebut saat selesai digunakan dengan memanggil C.free.

Pemanggilan ke C.CString mengembalikan sebuah pointer ke awal dari array char, jadi sebelum fungsi Print selesai kita mengonversi pointer tersebut dengan unsafe.Pointer dan melepaskan alokasi memori dengan C.free. Idiom umum dalam program cgo yaitu melakukan defer untuk pelepasan memori langsung setelah alokasi (terutama saat kode yang dibuat lebih kompleks daripada sebuah pemanggilan fungsi saja), seperti pada versi Print berikut:

func Print(s string) {
	cs := C.CString(s)
	defer C.free(unsafe.Pointer(cs))
	C.fputs(cs, (*C.FILE)(C.stdout))
}

Membangun paket-paket cgo

Untuk membangun paket-paket cgo, cukup gunakan 'go build’ atau 'go install’ seperti biasa. Perkakas Go mengenali perintah khusus import "C" dan secara otomatis menggunakan cgo untuk berkas-berkas tersebut.

Sumber lain untuk cgo

Dokumentasi perintah cgo berisi detail tentang paket-pseudo C dan proses pembangunannya. Contoh-contoh cgo dalam sumber kode Go mendemonstrasikan konsep tersebut lebih lanjut.

Terakhir, jika Anda penasaran bagaimana cgo bekerja secara internal, lihatlah komentar pendahuluan pada berkas cgocall.go pada paket runtime.