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
转载于:https://www.cnblogs.com/majianguo/p/6653919.html
go generate 生成代码相关推荐
- YII2使用Gii生成代码
Yii2 框架 之所以称之为高效快速开发的一款框架,是因为有一个神奇的工具Gii 用过Yii1框架的Coder都知道,Gii可以为你快速生成代码,也就是说搭建一个可以增删改查的WebApp可能一行代码 ...
- 如何让 Mybatis 自动生成代码,提高开发效率
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 在使用 mybatis 过程中, 当手写 JavaBean ...
- 简单的利用IDEA搭建SpringBoot+Maven+Mybatis+自动生成代码
最近在系统的学习SpringBoot框架,并且要用该框架做个项目--网上也大大小小看了很多教程,感觉很多写文章的人都不太负责任,只知道搬运,大概都没有实际操作过,问题也是有很多,所以自己写一篇文章记录 ...
- 【MyBatis】MyBatis自动生成代码之查询爬坑记
前言 项目使用SSM框架搭建Web后台服务,前台后使用restful api,后台使用MyBatisGenerator自动生成代码,在前台使用关键字进行查询时,遇到了一些很宝贵的坑,现记录如下.为展示 ...
- 使用Mybatis Generator自动生成代码
MyBatis Generator(MBG)是MyBatis MyBatis 和iBATIS的代码生成器. 它将为所有版本的MyBatis以及版本2.2.0之后的iBATIS版本生成代码. 它将内省数 ...
- 如何让 Mybatis 自动生成代码
在使用 mybatis 过程中, 当手写 JavaBean 和XML 写的越来越多的时候, 就越来越同意出错.这种重复性的工作, 我们当然不希望做那么多. 还好, mybatis 为我们提供了强大的代 ...
- SSM+Maven整合时在Eclipse中使用Mybatis逆向工程自动生成代码
场景 MybatisGenerator 官方文档 http://www.mybatis.org/generator/configreference/xmlconfig.html 实现 项目搭建好完整的 ...
- MyBatis逆向工程自动生成代码(附数据库表结构)
一.逆向工程介绍 逆向工程是一个专门为 MyBatis 框架使用者设计的代码生成器,可以根据数据库中的表字段名,自动生成 POJO 类,mapper 接口与 SQL 映射文件.支持基本的增删改查功能, ...
- 详解Dart中如何通过注解生成代码
简介:详解dart与java注解生成代码异同点 作者:闲鱼技术-龙湫 1.背景 最近在项目中使用到了Dart中的注解代码生成技术,这跟之前Java中APT+JavaPoet生成代码那套技术还是有一些不 ...
- 使用mybatis-generator自动生成代码的方法介绍及踩坑
mybatis-geneator是一款mybatis自动代码生成工具,可以通过配置,快速生成mapper和xml文件. 一. 使用maven插件 在pom.xml中添加mybatis-generato ...
最新文章
- python爬虫执行js代码_爬虫之python3用execjs执行JS代码
- pthread_detach 常规使用记录
- Linux下oracle数据库spfile参数配置文件丢失问题解决,“ORA-32001: write to SPFILE requested but no SPFILE is in use“问题处理
- 知识点讲解五:处理js异步加载问题
- 笔记本触摸板滑动(双指滑动)太快怎么设置?
- [linux命令技巧] mkdir -p
- [文档].Altera - Avalon接口规范
- 微信5.0登录提示服务器繁忙,iOS集成友盟社会化分享微信无法登录?
- win10查看所有的wifi密码。
- 腰部按摩操有两种做法
- stm32f103——基本定时器与定时器中断
- 个人网站申请域名怎么做?做网站申请域名多少钱?
- NET CORE读取Excel.xlsx单元格内的图片,并关联当前业务ID推送图片到指定服务器...
- 人工智能 六步走 学习路线
- 【Day12-Stream流Map集合】
- linux下配置调试debug
- XBee3与XBee S2C混合应用注意事项(石油A11领域)
- 对话|鲜丰水果:“看不见”的门店数字化
- 大三SE计组II开火车问题答案整理(第六章 自用
- 【接口平台设计】用例智能推荐
热门文章
- VirtualBox安装debian无法启动,正确的解决办法
- 头文件循环包含,导致找不到定义的类
- python POST发送多个段(如json消息+文件)
- 管理感悟:主管要怎样开会才正确
- python定时任务启动与停止_对Python定时任务的启动和停止方法详解
- 读取ANSYS结果文件中的数据C语言,[转载][转载]如何在ANSYS中读入txt文件的数据
- execl执行linux命令,Excel 调用Shell命令执行bash脚本和命令行代码
- react实现异步插件_初识react(四) react中异步解决方案之 redux-saga
- es的分片和副本_原创|ES广告倒排索引架构演进与优化
- idea shell 使用linux_Linux 基础操作