【reflect / testing】Golang Study Memo

今年の GWは javascript / golang / objective-c を書いています。まだまだ初心者ですが Golangを勉強した備忘録です。内容は以下です。

  • 標準パッケージを使ってみた感じ (os/exec, reflect, net/http)
  • 自作パッケージを作ってテストしてみた感じ

go version 1.2.1です。

Package os/exec

Goプログラムからのシェルコマンドの実行, C言語でいう system()とか exec()の機能を提供します。
下記例では, vmstat出力先の stdoutを bytes.Buffer型に紐づけるようにしています。

    cmd := exec.Command("vmstat")
    var out bytes.Buffer
    var stderr bytes.Buffer

    cmd.Stdout = &out
    cmd.Stderr = &stderr

    err := cmd.Run()
    if err != nil {
       fmt.Println(fmt.Sprint(err) + ": " + stderr.String())
    }

その後は文字列操作のstringsや文字変換のstrconvという便利なパッケージを使って料理します。
例えば,line, err := out.ReadString(‘\n’)で行を取得して,strings.Split(line, ” “)で部分文字列に分割して配列にいれたりという事が簡単にできて, c言語より圧倒的に楽でいい感じです。

Package reflect

reflectパッケージは型を取りだす機能を提供します。
変数に値を設定したり,関数に渡したinterface{}から静的な型情報を取得したりやjsonと構造体の相互変換ができるようです、

以下ではValueOf()でreflect.Value型にして,Kind()で型情報を取得しています。

package main

import (
    "fmt"
    "reflect"
)
 
func match(a, b interface{}) bool {
    aval := reflect.ValueOf(a) // Value Data
    if aval.Kind() != reflect.Ptr { // Type Data
        return false
    }

    bval := reflect.ValueOf(b)
    if aval.Elem().Kind() != bval.Kind() {
        return false
    }

    return true
}

func main() {
    var i int
    var j float32
    var k string

    t1 := match(&i, 24)
    fmt.Println("t1 : ",t1)

    t2 := match(&j, 15.4)
    fmt.Println("t2 : ",t2)

    t3 := match(k, "abc")
    fmt.Println("t3 : ",t3)
}

結果は以下になります。

$ go run reflect_example.go 
t1 :  true  # intとint
t2 :  false # float32とfloat64
t3 :  false # Ptrでない

reflect.MakeFuncを使うと、動的に関数を作ることができるようです。

Package net/http

続いて, net/httpのhttp-client機能を試してみました。

res, err := http.Get("[api-url]")
if err != nil {
    fmt.Println(fmt.Println(err))
} 
fmt.Println("http-header : ", res)
    
defer res.Body.Close()
    
body, err := ioutil.ReadAll(res.Body)
if err != nil {
    fmt.Println(fmt.Println(err))
} 
str := byteToString(body[:])
fmt.Println("http-body : ", str)

GET / POST も楽でいい感じです。
最近のLLはJITコンパイラの恩恵もありけっこう速いみたいだけど,コンパイラ言語なのでより速いことを期待。

goroutine / Channel

Go言語ではMessage-passing communicationというアプローチの並行処理プログラミングをサポートしています。
Channel で goroutine 間でのメッセージパッシングを試しました。

func rcvCannel(_ch chan bool) {
	/* reciever */
	for done := range _ch {
		fmt.Println(time.Now(), " : ", done)
	}
}
func main() {
	/* make channel*/
	ch := make(chan bool)

	go rcvCannel(ch)

	done := false

	for i := 0; !done; i++ {
		if i == 5 {
			done = true
		}
		/* sender*/
		ch <- done

		time.Sleep(time.Second * 1)
	}

	fmt.Println("done!")
}

自作パッケージ

klogという自作Loggerパッケージを例にします。
package main でなく package [自作パッケージ名] に変えます。

正しい手順ではないと思いますが,以下のディレクトリ構成にしました。
サブディレクトリに自作パッケージを置きます。

.
├── CHANGELOG.md
├── README.md
├── klog
│   └── klog.go
└── klog_test.go

Package testing

パッケージのテストはtestingを使います。
例えば, 自作したklogパッケージ内の func Printlog(func_n string) bool をテストしたい場合, func Test*** (t *testing.T)の *** にテストターゲットの関数名を入れます。

package klog

import (
  "testing"
  "./klog"
)

func TestPrintlog(t *testing.T) {
  /* expected TRUE test */
    actual := klog.Printlog("test")
    expected := true
    if actual != expected {
        t.Errorf("got %v\nwant %v", actual, expected)
    }
}

go run で実行しようとすると go run: cannot run *_test.go files と怒られるようにsuffixが_testのファイルはrunできません。
go test で実行します。

$ go test klog_test.go
ok  	command-line-arguments	0.026s

ベンチマークを取る場合は, func Benchmarkxxxと書いて -benchオプションを指定します。

$ go test -bench klog_test.go

色々いじった後は以下で整形するといい感じです。

$ gofmt -w  xxx.go
# recursive
$ find . -name '*.go' -exec gofmt -w {} \;

Reference

effective_goの翻訳サイトは若干情報が古い気がしています。FAQ - golang.jp をまず目を通しておくと良いと思います。
標準パッケージの情報を見る事が多いですが、最近の情報に関しては GoogleGroup や Gocon, Gokenの活動を見ています。

以下, Golangで書かれたプログラムです。勉強になります。


[1] Goの並行処理
[2] testing

* この記事はkarota-projectの活動に関する記事です。