今后一段时间要研究下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 生成代码相关推荐

  1. YII2使用Gii生成代码

    Yii2 框架 之所以称之为高效快速开发的一款框架,是因为有一个神奇的工具Gii 用过Yii1框架的Coder都知道,Gii可以为你快速生成代码,也就是说搭建一个可以增删改查的WebApp可能一行代码 ...

  2. 如何让 Mybatis 自动生成代码,提高开发效率

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 在使用 mybatis 过程中, 当手写 JavaBean ...

  3. 简单的利用IDEA搭建SpringBoot+Maven+Mybatis+自动生成代码

    最近在系统的学习SpringBoot框架,并且要用该框架做个项目--网上也大大小小看了很多教程,感觉很多写文章的人都不太负责任,只知道搬运,大概都没有实际操作过,问题也是有很多,所以自己写一篇文章记录 ...

  4. 【MyBatis】MyBatis自动生成代码之查询爬坑记

    前言 项目使用SSM框架搭建Web后台服务,前台后使用restful api,后台使用MyBatisGenerator自动生成代码,在前台使用关键字进行查询时,遇到了一些很宝贵的坑,现记录如下.为展示 ...

  5. 使用Mybatis Generator自动生成代码

    MyBatis Generator(MBG)是MyBatis MyBatis 和iBATIS的代码生成器. 它将为所有版本的MyBatis以及版本2.2.0之后的iBATIS版本生成代码. 它将内省数 ...

  6. 如何让 Mybatis 自动生成代码

    在使用 mybatis 过程中, 当手写 JavaBean 和XML 写的越来越多的时候, 就越来越同意出错.这种重复性的工作, 我们当然不希望做那么多. 还好, mybatis 为我们提供了强大的代 ...

  7. SSM+Maven整合时在Eclipse中使用Mybatis逆向工程自动生成代码

    场景 MybatisGenerator 官方文档 http://www.mybatis.org/generator/configreference/xmlconfig.html 实现 项目搭建好完整的 ...

  8. MyBatis逆向工程自动生成代码(附数据库表结构)

    一.逆向工程介绍 逆向工程是一个专门为 MyBatis 框架使用者设计的代码生成器,可以根据数据库中的表字段名,自动生成 POJO 类,mapper 接口与 SQL 映射文件.支持基本的增删改查功能, ...

  9. 详解Dart中如何通过注解生成代码

    简介:详解dart与java注解生成代码异同点 作者:闲鱼技术-龙湫 1.背景 最近在项目中使用到了Dart中的注解代码生成技术,这跟之前Java中APT+JavaPoet生成代码那套技术还是有一些不 ...

  10. 使用mybatis-generator自动生成代码的方法介绍及踩坑

    mybatis-geneator是一款mybatis自动代码生成工具,可以通过配置,快速生成mapper和xml文件. 一. 使用maven插件 在pom.xml中添加mybatis-generato ...

最新文章

  1. python爬虫执行js代码_爬虫之python3用execjs执行JS代码
  2. pthread_detach 常规使用记录
  3. Linux下oracle数据库spfile参数配置文件丢失问题解决,“ORA-32001: write to SPFILE requested but no SPFILE is in use“问题处理
  4. 知识点讲解五:处理js异步加载问题
  5. 笔记本触摸板滑动(双指滑动)太快怎么设置?
  6. [linux命令技巧] mkdir -p
  7. [文档].Altera - Avalon接口规范
  8. 微信5.0登录提示服务器繁忙,iOS集成友盟社会化分享微信无法登录?
  9. win10查看所有的wifi密码。
  10. 腰部按摩操有两种做法
  11. stm32f103——基本定时器与定时器中断
  12. 个人网站申请域名怎么做?做网站申请域名多少钱?
  13. NET CORE读取Excel.xlsx单元格内的图片,并关联当前业务ID推送图片到指定服务器...
  14. 人工智能 六步走 学习路线
  15. 【Day12-Stream流Map集合】
  16. linux下配置调试debug
  17. XBee3与XBee S2C混合应用注意事项(石油A11领域)
  18. 对话|鲜丰水果:“看不见”的门店数字化
  19. 大三SE计组II开火车问题答案整理(第六章 自用
  20. 【接口平台设计】用例智能推荐

热门文章

  1. VirtualBox安装debian无法启动,正确的解决办法
  2. 头文件循环包含,导致找不到定义的类
  3. python POST发送多个段(如json消息+文件)
  4. 管理感悟:主管要怎样开会才正确
  5. python定时任务启动与停止_对Python定时任务的启动和停止方法详解
  6. 读取ANSYS结果文件中的数据C语言,[转载][转载]如何在ANSYS中读入txt文件的数据
  7. execl执行linux命令,Excel 调用Shell命令执行bash脚本和命令行代码
  8. react实现异步插件_初识react(四) react中异步解决方案之 redux-saga
  9. es的分片和副本_原创|ES广告倒排索引架构演进与优化
  10. idea shell 使用linux_Linux 基础操作