go generate 生成代码
今后一段时间要研究下go generate,在官网博客上看了Rob Pike写的generating code,花了一些时间翻译了下。有几个句子翻译的是否正确有待考量,欢迎指正。
生成代码
通用计算的一个特性--图灵完备--是一个计算机程序可以编写一个计算机程序。这是一个强大的想法,尽管经常出现,但还不足够完美。例如,它是编译器定义的重要组成部分。它也是go test命令的工作原理:它扫描要测试的软件包,写出一个包含为包定制的测试工具的Go程序,然后编译并运行。现代电脑快到可以在几分之一秒完成这个看似昂贵的序列。
还有很多程序编写程序的其他例子。例如,yacc读入一个语法描述,并写出一个程序来解析该语法。Protocol buffer“编译器”读取接口描述输出结构定义,方法和其他支持代码。各种配置工具也是这样工作的,检查元数据或环境,输出自定义的本地配置。
因此,编写程序的程序是软件工程的重要组成部分,但是像yacc这些可以生成源代码的程序需要基础到构建过程中,以便可以编译它们的输出。当使用像Make这样的外部构建工具时,这通常可以很容易做到。但是在Go中,Go的工具从Go源中获取所有必要的构建信息,这有一个问题。没有机制可以单独地从go tool中运行yacc。
直到现在,就是这样。
最新的Go发布版,1.4,包含一个新命令,可以更轻松地运行这些工具。它叫做go generate,它可以通过扫描Go源码中的特殊注释来识别要运行的常规命令。了解go generate不是go build的一部分很重要。它不包含依赖关系分析,必须在运行go build之前显式运行。它旨在由Go package的作者使用,而不是其客户端。
Go generate命令很容易使用。作为一个预热,下面展示如何使用它来生成yacc语法。假设你有一个名为gopher.y的YACC输入文件,它定义了一种新语言的语法。要生成实现语法的Go源码文件,通常会调用Yacc的标准Go版本:
go tool yacc -o gopher.go -p parser gopher.y
-o选项命令输出文件,-p选项指定包名。
要使go generate驱动这个过程,在同一目录中的任何一个普通(非生成).go文件中,将该注释添加的文件中的任何位置:
//go:generate go tool yacc -o gopher.go -p parser gopher.y
这个文本就是上面的命令,前面加上一个由go generate识别的特殊注释。注释必须从行的开始处开始,并在在//和go:generate之间没有空格。在该标记之后,该行的其余部分指定go generate运行的命令。
现在运行它。切换到源目录,运行go generate,然后go build等等。
$ cd $GOPATH/myrepo/gopher
$ go generate
$ go build
$ go test
假设没有错误,go generate命令将调用yacc来创建gopher.go文件,此时目录包含完整的go源文件,因此我们可以正常构建,测试和正常工作。每次gopher.y被修改,只需要重新运行go generate来重新生成解析器。
有关go generate如何工作的更多详细信息,包括选项,环境变量等,可以参阅设计文档。
Go generate不会影响到make或其他一些编译机制,但它依附go tool,不需要额外安装,而且很适合Go生态系统。请记住,它是为package作者,而不是客户端,只是因为它调用的程序在目标机器上可能不可用。另外,如果包含的包是通过go get导入的,一旦文件被生成(并且被测试),他就必须被检入到源码库以供客户端使用。
现在有了go generate,可以用它来做新的事情。作为一个不同寻常的如何使用go generate的例子,有一个新的程序golang.org/x/tools仓库称为stringer。它可以自动为整数常量集合编写字符串方法。它不是发行版的一部分,但它很容易安装。
$ go get golang.org/x/tools/cmd/stringer
Stringer文档中有个示例,假设我们有一些包含一组定义不同类型的整形常数:
package painkiller
type Pill int
const (
Placebo Pill = iota
Aspirin
Ibuprofen
Paracetamol
Acetaminophen = Paracetamol
)
为了调试,我们希望变量很够很好地打印自己,这意味着我们需要一个具有如下签名的方法。
func (p Pill) String() string
手写很容易,也许是这样的:
func (p Pill) String() string {
switch p {
case Placebo:
return "Placebo"
case Aspirin:
return "Aspirin"
case Ibuprofen:
return "Ibuprofen"
case Paracetamol: // == Acetaminophen
return "Paracetamol"
}
return fmt.Sprintf("Pill(%d)", p)
}
当然还有其他的方法来写这个功能。我们可以使用一些Pill索引的字符串,或者map,或者其他一些技术。无论我们做什么,如果我们改变Pills集合,我们需要维护它来保证它是正确的。(Paracetamol的两个Name比其他的要棘手)。另外,采取哪种方法的问题取决于类型和值:有符号还是无符号,密集还是稀疏,基于零还是不基于零的等等。
Stringer程序负责处理所有这些细节。虽然它可以独立运行,但是它是由go generate驱动的要使用它,可以向源代码中添加生成注释,类型定义附近。
//go:generate stringer -type=Pill
此规则制定go generate 应运行stringer工具以生成Pill类型的String方法。输出会自动写入pill_string.go(默认情况下,我们可以使用 -output标志来覆盖)
运行之后
$ go generate
$ cat pill_string.go
// generated by stringer -type Pill pill.go; DO NOT EDIT
package pill
import "fmt"
const _Pill_name = "PlaceboAspirinIbuprofenParacetamol"
var _Pill_index = [...]uint8{0, 7, 14, 23, 34}
func (i Pill) String() string {
if i < 0 || i+1 >= Pill(len(_Pill_index)) {
return fmt.Sprintf("Pill(%d)", i)
}
return _Pill_name[_Pill_index[i]:_Pill_index[i+1]]
}
$
每次更改Pill或常量的定义时,我们需要做的就是运行go generate来更新String方法。当然,如果我们在同一个包中有多种类型设置了这种方式,单个命可以更新所有的String方法。
毫无疑问,生成的方法是丑的。但是,那是可以接受的,因为人类不需要做与它相关的工作,机器生成的代码通常是丑的。它努力做到高效率。所有的名称都在一个单一的字符串中,这样可以节省内存(即使有数十个也只有一个字符串头)。然后,一个数组,_Pill_index,通过一个简单高效的技术从值映射到名称。也请注意,_Pill_index是uint8的一个数组(不是一个切片),这是一个足够跨越值空间的最小整数。如果有更多的值,或者更少的,那么生产的_Pill_index类型可能会改变为uint16或者int8;无论什么都效果很好。
Stringer生成的Method使用的方法会根据常量集合的属性而变化。例如,如果常量是稀疏的,它可能会使用一个map。下面是一个基于常数集的常见例子,它代表了另外一种,
const _Power_name = "p0p1p2p3p4p5..."
var _Power_map = map[Power]string{
1: _Power_name[0:2],
2: _Power_name[2:4],
4: _Power_name[4:6],
8: _Power_name[6:8],
16: _Power_name[8:10],
32: _Power_name[10:12],
...,
}
func (i Power) String() string {
if str, ok := _Power_map[i]; ok {
return str
}
return fmt.Sprintf("Power(%d)", i)
}
简而言之,自动生成的method可以做到比人类做地更好。
在go tree中已经安装了go generate的许多其他用途。包括在unicode包中生产Unicode表,为encoding/gob创建有效的编解码方法,在time包中创建时区数据等等。
请创造性的使用go generate,鼓励动手实践。
即使没有,使用新的stringer工具为您的整形常量编写String方法。让机器做这样的工作。
By Rob Pike
go generate 生成代码的更多相关文章
- mybatis-generator:generate 生成代码配置踩坑详解
mybatis-generator:generate 生成代码配置踩坑不少,在此留下笔记以便后续填坑 一.mysql返回时间问题 错误信息: [ERROR] Failed to execute goa ...
- 问题:Custom tool error: Failed to generate code for the service reference 'AppVot;结果:添加Service Reference, 无法为服务生成代码错误的解决办法
添加Service Reference, 无法为服务生成代码错误的解决办法 我的解决方案是Silverlight+WCF的应用,Done Cretiria定义了需要在做完Service端的代码后首先运 ...
- mybatis 生成代码配置 mybatis-generator:generate 的使用详解
一.环境 mysql+eclipse 二.代码配置 pom.xml <?xml version="1.0" encoding="UTF-8"?> & ...
- Android注解使用之通过annotationProcessor注解生成代码实现自己的ButterKnife框架
前言: Annotation注解在Android的开发中的使用越来越普遍,例如EventBus.ButterKnife.Dagger2等,之前使用注解的时候需要利用反射机制势必影响到运行效率及性能,直 ...
- mybatis generator maven插件自动生成代码
如果你正为无聊Dao代码的编写感到苦恼,如果你正为怕一个单词拼错导致Dao操作失败而感到苦恼,那么就可以考虑一些Mybatis generator这个差价,它会帮我们自动生成代码,类似于Hiberna ...
- 使用mybatis-generator生成代码
文档地址: http://mbg.cndocs.tk/index.html 以下是一个简单的配置内容. 一.在maven配置文件中添加mybatis-generator插件 1 2 3 4 5 ...
- mybatis自动生成代码
使用maven集成mybatis-generator插件生成Mybatis的实体类,DAO接口和Map映射文件 本例中,使用的是mysql数据库 前提:表已经建好 mybatis框架的jar包,数据 ...
- 【转】- 使用T4模板批量生成代码
前言 之前在 “使用T4模板生成代码 - 初探” 文章简单的使用了T4模板的生成功能,但对于一个模板生成多个实例文件,如何实现这个方式呢?无意发现一个解决方案 “MultipleOutputHelpe ...
- maven插件mybatis-generator生成代码配置
鸣谢:http://my.oschina.net/u/1763011/blog/324106?fromerr=nJakGh4P (也可参看此博客进行配置) http://www.cnblogs.com ...
随机推荐
- 快速搭建一个本地的FTP服务器
快速搭建一个本地的FTP服务器 如果需要开发FTP文件上传下载功能,那么需要在本机上搭建一个本地FTP服务器,方便调试. 第一步:配置IIS Web服务器 1.1 控制面板中找到"程序& ...
- node实现一个WEBSOCKET服务器
早点时候翻译了篇实现一个websocket服务器-理论篇,简单介绍了下理论基础,本来打算放在一起,但是感觉太长了大家可能都看不下去.不过发现如果拆开的话,还是不可避免的要提及理论部分.用到的地方就简要 ...
- SQL Server Profiler的使用
最近一个项目,使用微软的Entity Framework的ORM框架的项目,部署到现场后,出现了系统缓慢,多个客户端的内存溢出崩溃的问题. 打开了SQL Server Profiler排查,发现有全表 ...
- CS Round#53 E Maxor
题意:给你N个数,你可以从中选出两个数将它们or起来得到M,求M的最大值及得到最大值的方案数. 刚了半个小时得到了一个貌似时O(N log max(Ai)^2)的方法,想了想发现貌似只能做出第一问,但 ...
- python学习第一天基础篇
学习背景:决定开始学习python之前,因为公司基本都是微软系统,所以很少碰到linux系统,机缘巧合接到了一个项目是使用shell对mysql进行backup,因为公司唯一的系统工程师是微软在行,对 ...
- centos 6.5 安装mongodb2.6
前言: 系统版本号:Centos-6.5-x86_64 *** Centos编译安装mongodb 2.6 系统最好是64位的,才干更好发挥mongodb的性能 1.准备,下载源文件(二进制编译版) ...
- day01_使用Android Studio创建第一个Android项目
使用Android Studio开发Android项目如此简单 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize ...
- SpringBoot ( 七 ) :springboot + mybatis 多数据源最简解决方案
说起多数据源,一般都来解决那些问题呢,主从模式或者业务比较复杂需要连接不同的分库来支持业务.我们项目是后者的模式,网上找了很多,大都是根据jpa来做多数据源解决方案,要不就是老的spring多数据源解 ...
- 《程序员的职业素养》【PDF】下载
<程序员的职业素养>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382243 内容介绍 <程序员的职业素养>是编程大 ...
- javaWeb超链接(href)请求-特殊字符处理
写在前面: 最近在项目中,遇到一个问题,在点击一个超链接时,页面报错.通过浏览器调试就可以知道发送的请求参数是不完整的,因为参数中含有特殊字符.所以就报错啦~~ 原代码,不能正确发送含有特殊字符的参数 ...