Go言語からC言語の関数を呼び出す
随分久しぶりの更新です。どうにか進路は決まったらしいです。
さて、つい最近登場し注目を集めたGo言語ですが、C言語のライブラリをリンクして呼び出せる機能があり、この機能を使えば、Cで書かれたライブラリのGoラッパー(Goバインディング)が作れます。わかってしまえば比較的簡単にできますが、猛烈に苦労したので今回はその悪戦苦闘の軌跡の前半戦ぐらいを記しておきます。
なお、Goに限りませんが、英語ではいわゆるラッパーのことをFFI(Foreign Function Interface)という呼び方が良くされています。ググる際は使ってみましょう。
Goのインストール
以下のページを参照。
出たばかりなのにこんなに日本語の情報が揃ってる言語なんてなかなか無いのではないでしょうか。Googleの圧倒的な注目度を痛感しますね。とはいえ、やはり生まれたてということで絶対的な情報量は少ないです。困ったらソースを見に行くぐらいの覚悟は当然必要でしょう。
cgo
cとgoの間を取り持ってくれるのがcgoです。Cの関数呼び出しを含んだGoのコードをcgoに渡すと、自動的にうまいことやってくれて、CとGoの橋渡しをするような.cや.goファイルを出力してくれます。
最初の例として、Cのputs関数をGoから呼び出してみます。以下のようなGoプログラム(mylib.go)を書きます。
package mylib /* #include <stdio.h> #include <stdlib.h> */ import "C" import "unsafe" func Puts(s string) { p := C.CString(s) // GoのstringからCの文字列(char*)に変換 C.puts(p) C.free(unsafe.Pointer(p)) // pの領域を解放 }
cgoは実に面白い仕組みになっています。import "C"がcgoを使う合図になっていますが、その上のコメントはただのコメントではありません。import "C"のすぐ上に書いたコメントはCのコードとして認識され、それに従いcgoはCの関数を呼び出すためのコードを自動生成します。C.putsのような記述は適当な呼び出しの形式に書き換えられます。cgoの呼び出し方はいたって単純。
cgo mylib.go
実行すると、_cgo_defun.cやmylib.cgo1.goなど5個程度のファイルが生成されます。これらを8gや8cによりコンパイルすればライブラリファイルが得られるようです。しかし手作業でやるのは面倒です。
Makefile
普通はMakefileを使ってビルドを自動化するので、cgoを自分で打ち込むことは普段はほとんどないでしょう。特にGoは、ちょっとしたプログラムでも実行ファイルを得るまでの手順が面倒なので、最初のうちからすべてmakeで済ませるようにしましょう。以下のようなMakefileを作っておきます。
CGOFILES=mylib.go TARG=mylib include $(GOROOT)/src/Make.$(GOARCH) include $(GOROOT)/src/Make.pkg
このincludeのおかげでうまいことやってくれるようです。以降はmake一発で、cgoに加えてコンパイルしてライブラリファイルを生成するまでを自動化できます。
make
makeに成功すると、_objディレクトリの中にはmylib.aのようなライブラリファイルが作られているはずです。これを利用側のコードとリンクすれば先程定義したPuts関数が使えるというわけです。
利用する
利用側のコードは例えば次のようになります (mylib_test.go)。
package main import "mylib" func main(){ mylib.Puts("Hoge") }
さてライブラリとのリンクですが、これがコマンド手打ちだとなかなかうまくいきませんでした。面倒なのでこれもmakeに任せます。Makefileに何行か追加します。
CGOFILES=mylib.go TARG=mylib include $(GOROOT)/src/Make.$(GOARCH) include $(GOROOT)/src/Make.pkg mylib_test: install mylib_test.go $(GC) mylib_test.go $(LD) -o $@ mylib_test.$O #Oはオーです # ここの空白はタブ1つ
こうしておけば、以下のコマンドで mylib_test の実行ファイルが得られます。(mylibに変更があればリメイクされます。)
make mylib_test
実行ファイルは同じディレクトリに mylib_test として出力されています。
注意点
とりあえず今回はここまで。これだけではまだまだ何も作れないのでそれは次回以降となりますが、コンパイルさえ通れば後はなんとかなるでしょう。私はここまでで5時間ほど潰しました。
簡単な注意点をいくつか。
- 改行にうるさい
- 関数やifなどの後の中括弧 { は同じ行に書く。改行するとアウト
- コードの最後は改行が入っていなければならない。このエラーがかなり頻発。
- 使っていないimportや変数があると、警告ではなくエラー。コンパイル失敗。
- ライブラリとして外部に公開したい関数や構造体などは、名前の先頭を大文字にしなければ外部から参照できない。
特に一番下は随分とハマりました。