Edited at

Go で EventEmitter のコードを自動生成するツールを作った

More than 1 year has passed since last update.


前置き

Go で EventEmitter/Dispatcher 的なことをしたい場合、 emissiongo-pubsub などの既存のライブラリを使うのが最も手っ取り早いと思いますが、

golang で event emitter/dispatcher 的なもの | tech - 氾濫原


リフレクションで型変換は隠蔽されているが、もしリスナーとエミッターとで型が食い違っていると、実行時エラーになる。

「イベント名」というパラメータが存在せず、型の選択によって自動的にディスパッチされる。よって型がマッチしなければリスナーは実行されないので、原理的に型変換の実行時エラーは発生しない (もし間違えた場合単に実行時になって「呼ばれない」ことに気付く)


で解説されている通り、上記ライブラリではコードにミスがあった場合、実行時でないとその問題が露見しません。

やはり静的型付け言語を書いている以上、コンパイル時にミスは発見できてほしい…ということで、コンパイル時に型チェックできるような EventEmitter を go generate で自動生成するツールがあったら、それなりに需要があるんじゃないだろうかと思い、作ってみました。

https://github.com/shiwano/genem


インストール

$ go get -u github.com/shiwano/genem


使い方

ヘルプテキストをべたり。

genem

Usage:
genem [options] <file>
genem -h | --help
genem --version

Options:
-n, --emitter-name string Specify the event emitter type name (default: EventEmitter).
-o, --output-file-name string Specify the output file name (default: event_emitter.go).
-p, --print Print the generated code without file output.
-h, --help Output help information.
-v, --version Output version.

このツールは、指定した Go のファイルから lower camel case で最後に Event で終わる名前の struct を探してきて(例: fooEvent)、その struct の定義に対応した EventEmitter のコードを自動生成します。


例えば

以下のようなファイルを作り、

//go:generate genem -n Emitter -o emitter.go $GOFILE

package event

type fooEvent struct {
bar string
baz int
}

type quxEvent struct {
quux float32
}

go generate をすると、そのファイルのあるディレクトリに emitter.go というファイル名で以下のようなコードが自動生成されます。

package event

type Emitter struct {}

func NewEmitter() *Emitter

func (_e *Emitter) EmitFooEvent(bar string, baz int)
func (_e *Emitter) AddFooEventListener(listener func(bar string, baz int))
func (_e *Emitter) AddFooEventListenerOnce(listener func(bar string, baz int))
func (_e *Emitter) RemoveFooEventListener(listener func(bar string, baz int))

func (_e *Emitter) EmitQuxEvent(quux float32)
func (_e *Emitter) AddQuxEventListener(listener func(quux float32))
func (_e *Emitter) AddQuxEventListenerOnce(listener func(quux float32))
func (_e *Emitter) RemoveQuxEventListener(listener func(quux float32))

生成された EventEmitter は、以下のような感じで使えます。

package main

import (
"fmt"
"example.com/event"
)

func main() {
e := event.NewEmitter()
e.AddFooEventListener(func(bar string, baz int) {
fmt.Println("foo: ", bar, baz) // Output: "foo: bar 3"
})
e.EmitFooEvent("bar", 3)
}