今年(2015年)に流行った「進捗・どう・です・か」をcgo + stringerで作ってみる。

2015年、エンジニア界隈で話題になった事といえばなんでしょうか。
自分は「[進捗どうですか](http://elephnote.com/blog/archives/936)」が印象に残っています。
[いろんな言語](http://qiita.com/B73W56H84/items/a6c4b770c6efc8d1af35#%E3%81%82%E3%82%8F%E3%81%9B%E3%81%A6%E8%AA%AD%E3%81%BF%E3%81%9F%E3%81%84)で実装されましたね。

([golang の進捗どうですか](http://qiita.com/mattn/items/4b4ce89396d660a155a9))もすでにあって、自分は乗り遅れたので傍観者として見ていたのですが、今年を代表するネタですので今年最後、自分も乗っかろうとcgoで乗ってみました。

# [cgo](https://golang.org/cmd/cgo/)

端的に言えばgolangとC言語の間で相互に関数を使い合うためのパッケージです。

```go:cgo.go
package main

/*
#include <stdio.h>
void hello_world() {
	printf("%s\n", "Hello World!!");
}
*/
import "C"

func main() {
	C.hello_world()
}
```
上記はgolangからC言語の一方通行です。
最初はこんな感じで作ろうかと血迷ってましたが「それじゃC言語のアドベントカレンダーだろ！」と理性を取り戻し、使った事が無かったstringerと組み合わせてみることにしました。

# [stringer](https://godoc.org/golang.org/x/tools/cmd/stringer)

端的に言えばgolangの定数(iotaを使ったもの)の定数名を取得できるコマンドです。
「golangの進捗どうですか」でも使われています。

```go:main.go
package main

import (
	"fmt"
)

type マキシマムザホルモン int

const (
	マキシマムザ亮君 マキシマムザホルモン = iota
	上ちゃん
	ダイスケはん
	ナヲ
)

func main() {
	var mth マキシマムザホルモン = ダイスケはん
	fmt.Println(mth)
}
```

このままだと`2`としか表示されません。
どうしても`ダイスケはん`って表示したい！！
そんな時にstringer

```bash
$ go get -u -v golang.org/x/tools/cmd/stringer
$ stringer -type マキシマムザホルモン main.go
```
上記コマンドを打つと下記のような`.go`ファイルが生成されます。

```go:マキシマムザホルモン_string.go
// Code generated by "stringer -type マキシマムザホルモン main.go"; DO NOT EDIT

package main

import "fmt"

const _マキシマムザホルモン_name = "マキシマムザ亮君上ちゃんダイスケはんナヲ"

var _マキシマムザホルモン_index = [...]uint8{0, 24, 36, 54, 60}

func (i マキシマムザホルモン) String() string {
	if i < 0 || i >= マキシマムザホルモン(len(_マキシマムザホルモン_index)-1) {
		return fmt.Sprintf("マキシマムザホルモン(%d)", i)
	}
	return _マキシマムザホルモン_name[_マキシマムザホルモン_index[i]:_マキシマムザホルモン_index[i+1]]
}
```
これで実行すれば`ダイスケはん`が現れます。

```bash
$ go run *.go 
ダイスケはん
```
余談ですが、ファイル名もコマンド実行時に`-output`オプションで指定できるので、型名が日本語の時は指定することを強くオススメしますｗ

```bash
$ stringer -type マキシマムザホルモン -output mth_string.go main.go
```

# 作る

単にC言語で作ったプログラムを呼んでもなーと思ったので相互に呼び合うプログラムにしました。

```go:wap.go
package wap

/*
extern void wap(void);
*/
import "C"
import "fmt"

type word int

const (
	進捗 word = iota
	どう
	です
	か
)

//export printWord
func printWord(num C.int) C.int {
	fmt.Print(word(num))

	return num
}

// Wap is "what about progress?"
func Wap() {
	C.wap()
}
```

```c:wap.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "_cgo_export.h"

void wap(void) {
  srand(time(NULL));
  int w = 0, n = 0;

  for (;;) {
    n++;

    int s = printWord(rand() % 4);

    if (w != s) {
      if (s == 0) {
        w = 1;
      } else {
        w = 0;
      }
    } else {
      w++;
      if (w == 4) {
        break;
      }
    }
  }

  printf("%s",
         "\n"
         "＿人人人人人人人＿\n"
         "＞進捗どうですか＜\n"
         "￣Y^Y^Y^Y^Y^Y^Y￣\n");
  printf("%d回で煽られました\n", n);
}
```
golangから呼んでいるC言語関数が`wap(void)`でC言語から呼んでいるgolang関数が`printWord(num C.int)`です。
stringer効果で`fmt.Print(word(num))`と書くだけで「進捗」「どう」「です」「か」のどれかが表示されるようになっています。

苦労したというかできなかったのは、最初、mainパッケージで作っていたのですが、
下記のようなエラーが出て動かなくて困りました。

```
# command-line-arguments
Undefined symbols for architecture x86_64:
  "_wap", referenced from:
      __cgo_57c8a91b469e_Cfunc_wap in wap.cgo2.o
     (maybe you meant: __cgo_57c8a91b469e_Cfunc_wap)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
```
[Re: [go-nuts] Cgo and libccv: "Undefined symbols"](https://groups.google.com/forum/#!topic/golang-nuts/yKdKe2rZDbg)
とか読むとフラグがあれば行けるっぽいけど
とりあえず、[cgo · golang/go Wiki](https://github.com/golang/go/wiki/cgo#global-functions)を読んでmainとは別のパッケージにするという方法で実装しました。
パッケージにできたので誰でも使えるようになったのはよかったかなと思います。(誰が使うんだ！)

# できた

[github.com/178inaba/wap](https://godoc.org/github.com/178inaba/wap)

即試したい場合はexampleディレクトリで`go run main.go`をしてもらうと進捗を聞いてくれます。

```bash
$ go get -u github.com/178inaba/wap
$ cd $GOPATH/src/github.com/178inaba/wap/example
$ go run main.go
進捗進捗どう進捗ですか進捗進捗ですかですです進捗ですですです進捗かですかか進捗か進捗どうですどう進捗進捗進捗ですどうどうどうどうどう進捗どうか進捗どうかです進捗ですですですかかかです進捗どうです進捗かかかどう進捗進捗どうどうか進捗ですか進捗かかか進捗どうかですですか進捗どう進捗どうどう進捗進捗どうですか
＿人人人人人人人＿
＞進捗どうですか＜
￣Y^Y^Y^Y^Y^Y^Y￣
87回で煽られました
```

パッケージ化してあるのでexampleの`main.go`の中身は`wap.Wap()`を呼んでいるだけです。

```bash
package main

import "github.com/178inaba/wap"

func main() {
	wap.Wap()
}
```

みなさんのプログラム内でも`wap.Wap()`と書いていただければ進捗を聞きますよｗ
コマンドラインツールにいかがでしょうか？ｗｗｗ

ちなみに「wap」は「what about progress? (進捗どうですか？)」の略です。

# 最後に
みなさん、2015年の**進捗どうですか**？