15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Go4Advent Calendar 2019

Day 6

Cgo を使って C 言語で書かれた関数をテストする

Last updated at Posted at 2019-12-05

この記事は Go4 Advent Calendar 2019 の 6 日目の記事です。

Cgo を使うと C 言語で書かれた関数を定義したり呼び出したりできます。これを使って、 C 言語で書かれた関数をデバッグしてみます。

背景

組込開発 (C 言語) において、 AES を用いた処理があり検算が面倒だった。 Go だと自分の中ではサクッと書けるので、 C 言語で作ったものを Go で検算するようにしてみたら快適だったので共有しようと考えた。

Cgo とは

Cgo enables the creation of Go packages that call C code.
https://golang.org/cmd/cgo/

Cgo の簡単なサンプル

Go では import "C" の直前のコメントに C 言語ソースを書くことで、手軽に C 言語ソースと接続することができます。
直前に、というのがポイントで、 import "C" の上に空行を挟むと動かないので注意。

package main

/*
int max(int x, int y) {
	return x < y ? y : x;
}
*/
import "C"

import (
	"fmt"
)

func main() {
	num := C.int(1)
	ret := C.max(num, 2)

	fmt.Println("max(1, 2) => ", ret)
	fmt.Printf("type(ret) : %T\n", ret)

	i := int(ret)
	fmt.Printf("type(i) : %T\n", i)
}

C で書かれた max という関数は、 Go の世界からは C.max という名前でコールすることが可能です。

この時点で実行すると以下のような結果になります。
戻り値は見慣れない型 main._Ctype_int になっていますが、 int 型にキャストして受け取れば問題ないです。

$ go build && go-cgo.exe
max(1, 2) =>  2
type(ret) : main._Ctype_int
type(i) : int

このような形でシンプルな C ソースは Go から簡単に使う事が出来ます。
また、 Go の関数を C からも使うことができるので、興味がある人は以下を読むと良いです。
struct を使うケースや char * と Go のstring の変換なども書かれています。

C で書かれた max 関数をテストする

上で作成した C.max() をテストしようとして以下のようなソースを書いたとしてもそのままでは実行できません。

package main

import "testing"

func TestCgoMax(t *testing.T) {
	if g, e := 3, C.max(2, 3); g != e {
		t.Errorf("got %d, want %d", g, e)
	}
}

実際に実行すると以下のようになります。

$ go test -v
# github.com/sago35/go-cgo [github.com/sago35/go-cgo.test]
.\main_test.go:6:16: undefined: C
FAIL    github.com/sago35/go-cgo [build failed]

undefined: C と出るので、テストコード内に import "C" 等を書いても以下のように怒られます。 Cgo はテストコード内では使えないようです。

$ go test -v
can't load package: package github.com/sago35/go-cgo: use of cgo in test C:\Users\takasago.masaaki\dev\src\github.com\sago35\go-cgo\main_test.go not supported

ということで、 Go で wrap してみましょう。テストコード側も、新しく作った cgoMax() を呼ぶようにしてみます。

package main

/*
int max(int x, int y) {
	return x < y ? y : x;
}
*/
import "C"

import (
	"fmt"
)

func cgoMax(x, y int) int {
	cx := C.int(x)
	cy := C.int(y)
	ret := C.max(cx, cy)
	return int(ret)
}
package main

import "testing"

func TestCgoMax(t *testing.T) {
	if g, e := 3, cgoMax(2, 3); g != e {
		t.Errorf("got %d, want %d", g, e)
	}
}

実行してみると、うまくテストできるようになりました。

$ go test -v
=== RUN   TestCgoMax
--- PASS: TestCgoMax (0.00s)
PASS
ok      github.com/sago35/go-cgo        0.070s

まとめ

C 言語で書かれたもののテストを Go で行うことができました。
Go でライブラリを書いていると、できるだけ pure Go で書こうと思ったりしちゃうわけですが、 Cgo もとても便利なので使えるようになっておくとよさそう。

15
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?