东抄西抄拼装而成的学习笔记,go 版本为 1.12.7,自动生成代码的技术还是非常方便的
Hello world go 的命令行工具 go generate
可以快速生成一些逻辑的代码,先写一个 hello world 的例子
1 2 3 4 5 6 7 8 9 10 package mainfunc main () {}
执行 go generate main.go
,会有如下的输出
1 2 3 4 5 hello world amd64 darwin main.go main
从第二行开始就是一些 go generate
可以利用的和 go 有关的环境变量
这里记录两个例子:
模拟泛型 此例子取自《go in practice》
这里的程序逻辑就是一个队列,FIFO,但是希望可以由许多中类型使用。由于 go 中没有泛型,所以最安全的做法就是每种类型写一个队列实现,但是它们逻辑相同。
可以考虑使用 go generate
+ template 来实现,先为该逻辑编写模板,非常简单的队列逻辑
完整的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package mainimport ( "fmt" "html/template" "os" "strings" ) var tpl = `// Code generated, DO NOT EDIT. package {{.Package}} type {{.MyType}}Queue struct { q []{{.MyType}} } func New{{.MyType}}Queue() *{{.MyType}}Queue { return &{{.MyType}}Queue{ q: []{{.MyType}}{}, } } func (o *{{.MyType}}Queue) Insert(v {{.MyType}}) { o.q = append(o.q, v) } func (o *{{.MyType}}Queue) Remove() {{.MyType}} { if len(o.q) == 0 { panic("empty queue") } first := o.q[0] o.q = o.q[1:] return first } ` func main () { tt := template.Must(template.New("queue" ).Parse(tpl)) for i := 1 ; i < len (os.Args); i++ { var ( dest = strings.ToLower(os.Args[i]) + "_queue.go" err error file *os.File ) if file, err = os.Create(dest); err != nil { if !os.IsExist(err) { fmt.Printf("Could not create %s: %s (skip)\n" , dest, err) continue } _ = os.Remove(dest) } vals := map [string ]string { "MyType" : os.Args[i], "Package" : os.Getenv("GOPACKAGE" ), } _ = tt.Execute(file, vals) _ = file.Close() } }
最后在生成模板的时候,将 MyType
替换成用户指定的类型,而 Package
则会替换成调用该程序的文件所在的包名,举个例子:
1 2 3 4 ── practiceuse ├── main.go ├── myint_queue.go └── queue
queue
为自动生成代码的程序,main.go
为自动生成代码的程序的调用方,而 myint_queue.go
为自动生成的文件,它的包名和 main.go
相同,main.go
函数的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport "fmt" type MyInt int func main () { var one, two, three MyInt = 1 , 2 , 3 q := NewMyIntQueue() q.Insert(one) q.Insert(two) q.Insert(three) fmt.Printf("First value: %d\n" , q.Remove()) }
执行 go generate main.go
生成的 myint_queue.go
的内容如下,这个逻辑就是根据模板生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package maintype MyIntQueue struct { q []MyInt } func NewMyIntQueue () *MyIntQueue { return &MyIntQueue{ q: []MyInt{}, } } func (o *MyIntQueue) Insert (v MyInt) { o.q = append (o.q, v) } func (o *MyIntQueue) Remove () MyInt { if len (o.q) == 0 { panic ("empty queue" ) } first := o.q[0 ] o.q = o.q[1 :] return first }
快速生成Web服务状态码 该部分摘自 深入理解Go之generate
有一个需求如下,给出一个 go 文件:
1 2 3 4 5 6 7 8 9 10 11 package errorcodetype ErrCode int const ( OK ErrCode = iota INVALID_DATA UNAUTHORIZED INTERNAL_ERROR NOT_FOUND )
希望能根据它为 ErrCode
自动实现接口 Stringer
,并且打印出的信息为注释的内容,比如下面的出处为 ”无权限“
1 fmt.Println(errorcode.UNAUTHORIZED)
此处可以使用 Go 官方提供的 Go Tools: stringer ,执行 go get golang.org/x/tools/cmd/stringer
或者 http_proxy=... go get -u golang.org/x/tools/cmd/stringer
使用代理下载,之后将其加入环境变量,就可以直接在命令行中调用 stringer
了。
将上述文件做如下修改
1 2 3 4 5 6 7 8 9 10 11 12 package errorcodetype ErrCode int const ( OK ErrCode = iota INVALID_DATA UNAUTHORIZED INTERNAL_ERROR NOT_FOUND )
-type
指定需要实现 Stringer
接口的类型名称,而 -linecomment
表示输出的将行注释作为输出的内容,自动生成的文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package errorcodeimport "strconv" func _() { var x [1 ]struct {} _ = x[OK-0 ] _ = x[INVALID_DATA-1 ] _ = x[UNAUTHORIZED-2 ] _ = x[INTERNAL_ERROR-3 ] _ = x[NOT_FOUND-4 ] } const _ErrCode_name = "OK无效参数无权限服务器内部错误无该资源" var _ErrCode_index = [...]uint8 {0 , 2 , 14 , 23 , 44 , 56 }func (i ErrCode) String () string { if i < 0 || i >= ErrCode(len (_ErrCode_index)-1 ) { return "ErrCode(" + strconv.FormatInt(int64 (i), 10 ) + ")" } return _ErrCode_name[_ErrCode_index[i]:_ErrCode_index[i+1 ]] }