Go から Cを呼び出す

Go には2つコンパイラの系統がある。6gなどのgcと呼ばれるものとgccgoと呼ばれるもの。前者はcと呼び出しのコンベンションが違うので、Cのライブラリを直接呼び出すことはできない。後者はgccへのトランスレータ?なので呼び出すことができるようだ。

6g系列でCのライブラリを呼びだすにはラッパとなるgoプログラムを書き、それをcgoというプログラムで処理して中間となる.goと.cをつくって、.cから.soをつくりそれをインストールしておくという手間がかかる。でもこれを自動化してくれる Makefileが用意されているのでこれを使えばよい。

Cの関数の用意

まず呼び出されるCの関数を用意する。今回は足し算。ヘッダファイルと本体。

/* add.h */
int add(int a, int b);
/* add.c */
#include "add.h"
int add(int a, int b) {
    return a + b;
}

これをコンパイルしてadd.oを作っておく。

呼び出しラッパ

この関数をパッケージでラップしてやる。ここではforeignTestというパッケージにした。

package foreignTest

/*
#include <stdio.h>
#include "add.h"
*/
import "C"

func Add(a int, b int) int {
     return int(C.add(C.int(a), C.int(b)))
}

ポイントは3つ。

インポート文直前のコメント

import "C" の直前に書いたコメントはCのコードとして解釈される。ここではインポート文を書いたが、普通の関数定義なんかも書ける。

Cパッケージ

Cの関数は"C"というパッケージの関数として見える。したがって、C.add がCでのaddの呼び出しになる。

型変換

add を呼び出している部分で、C.int(a) などとしているのは、goのintからCのintへの型変換。同様に戻り値もint()でgoのintに戻している。

Makefile

Makefileはこんな感じ。CGO_LDFLAGS で Cのライブラリを指定している。

include $(GOROOT)/src/Make.inc

TARG=foreignTest
CGOFILES=\
	foreignTest.go

CLEANFILES+=foreignTest 
CGO_LDFLAGS=add.o

include $(GOROOT)/src/Make.pkg

%: install %.go
	$(GC) $*.go
	$(LD) -o $@ $*.$O

つぎのようにするとパッケージがビルドされ、インストールされる。

gomake install

テスト

テストプログラムはこんな感じ。

package main
import "foreignTest"
import "fmt"

func main() {
     fmt.Printf("%d\n", foreignTest.Add(1, 2));
}

これで

gomake main

でOK。

所感

結構面倒だが、ツールがよくできているのでそれほどストレスは無いかな。でもCからGoを呼び出すのはどうやるんだろう?できないのかな?