読者です 読者をやめる 読者になる 読者になる

アルパカ三銃士

〜アルパカに酔いしれる獣たちへ捧げる〜

cgo をちゃんと使ってみる!!

cgo を使ってみます

cgo って何?という時にここを読むと良さそうです!

コードを書いてみる

cgo では C.C言語の関数() とすることで, C言語から関数を呼び出すことができます. 実際に簡単な print を行うコードを書いてみます. ちなみに OS は Mac OSX 10.11.6 です.

package main

/*
#include <stdio.h>
void print(const char *str) {
   printf("%s", str);
}
*/
import "C"

func main() {
    str := C.CString("Hello, World\n")
    C.print(str)
}

出力結果は

Hello, World

となります. 始め

C.printf(C.CString("Hello, World\n"))

という風に書いてましたが, unexpected type: ... というエラーが出てコンパイルができませんでした. これは僕の予想ですが, printf(1) の引数は次のようになっているからだと考えました.
int printf(const char * __restrict, ...) 事実はわからないので, 知ってる方教えて欲しいです.

C で得られた結果を Go で扱ってみる

package main

/*
package main

/*
#include <string.h>
int num(int n) {
   return 10 + n;
}

float flt(float f) {
   return f + 20.5;
}

char *str(char *s) {
   return strcat(s, " Good!!");
}
*/
import "C"
import "fmt"

func main() {
    n := C.num(10)
    fmt.Println(n)
    f := C.flt(20.78)
    fmt.Println(f)
    s := C.str(C.CString("Condition is"))
    fmt.Println(C.GoString(s))
}

得られた出力結果です.

20
41.28
Condition is Good!!

結構簡単にやり取りできますが, C で扱う文字列と Go で扱う文字列は型が異なるため, C.GoString を利用して C 側で得られた文字列の結果を Go の文字列へ変換してあげることがポイントです.

Go から C の Macro を呼び出してみる

Macro に文字列, をしていしてる場合は次のように Go の文字列として扱うことができるようです.

package main

/*
#define Hello "Hello"
#define INT 10
#define CHR 'A'
*/
import "C"
import "fmt"

func main() {
    fmt.Println(C.Hello, C.INT, C.CHR)
}

結果は次のようになります.

Hello 10 65

しかし, まだ対応してないのか #define FLOAT 1.234 のような Macro を呼び出そうとするとエラーになります.

C の errno を Go の error として扱う

cgo のドキュメントには次のような記述があります.

Any C function (even void functions) may be called in a multiple assignment context to retrieve both the return value (if any) and the C errno variable as an error (use _ to skip the result value if the function returns void).

試してみましょう.

package main

/*
#include <math.h>
*/
import "C"
import (
    "fmt"
    "log"
)

func main() {
    n, err := C.sqrt(-1)
    //n, err := C.sqrt(4)
    if err != nil {
        log.Fatal(err.Error())
    }
    fmt.Println(n)
}

結果は

NaN

ちゃんとエラーを得ることができているみたいですね!