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

More than 3 years have passed since last update.

2015年、エンジニア界隈で話題になった事といえばなんでしょうか。

自分は「進捗どうですか」が印象に残っています。

いろんな言語で実装されましたね。

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


cgo

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


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

端的に言えばgolangの定数(iotaを使ったもの)の定数名を取得できるコマンドです。

「golangの進捗どうですか」でも使われています。


main.go

package main

import (
"fmt"
)

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

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

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


このままだと2としか表示されません。

どうしてもダイスケはんって表示したい!!

そんな時にstringer

$ go get -u -v golang.org/x/tools/cmd/stringer

$ stringer -type マキシマムザホルモン main.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]]
}


これで実行すればダイスケはんが現れます。

$ go run *.go 

ダイスケはん

余談ですが、ファイル名もコマンド実行時に-outputオプションで指定できるので、型名が日本語の時は指定することを強くオススメしますw

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


作る

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


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()
}



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"

とか読むとフラグがあれば行けるっぽいけど

とりあえず、cgo · golang/go Wikiを読んでmainとは別のパッケージにするという方法で実装しました。

パッケージにできたので誰でも使えるようになったのはよかったかなと思います。(誰が使うんだ!)


できた

github.com/178inaba/wap

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

$ 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()を呼んでいるだけです。

package main

import "github.com/178inaba/wap"

func main() {
wap.Wap()
}

みなさんのプログラム内でもwap.Wap()と書いていただければ進捗を聞きますよw

コマンドラインツールにいかがでしょうか?www

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


最後に

みなさん、2015年の進捗どうですか