1. 178inaba

    Posted

    178inaba
Changes in title
+今年(2015年)に流行った「進捗・どう・です・か」をcgo + stringerで作ってみる。
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,194 @@
+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`オプションで指定できるので、型名が日本語の時は指定することを強くオススメしますw
+
+```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)
+パッケージ化してあるのでみなさんのプログラム内に`wap.Wap()`と書いていただければ進捗聞きますよw
+
+# 最後に
+みなさん、2015年の**進捗どうですか**?