代码生成利器 - Go Generate

简介: 代码生成利器 - Go Generate

简介

  在Go语言中,go generate​ 命令是一个非常有用的工具,它可以帮助我们自动化地生成代码。本文将详细介绍如何使用 go generate​ 命令,并提供一些示例来说明它的用法。

什么是Generate命令?

  ​go generate​ 命令是一个用于自动化生成Go代码的工具。它可以在Go源文件中的特殊注释中指定命令,然后在运行 go generate​ 命令时自动执行这些命令。这些命令可以用来生成代码、格式化代码、运行测试等等。

  在Go 1.4版本中,go generate​ 命令首次发布,它的目的是为了解决Go语言中的一些重复性工作。自从发布以来,它已经成为了一个非常受欢迎的工具,广泛应用于Go语言的开发中。

语法

go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]
  • generate 由现有指令描述的运行命令文件。这些命令可以运行任何进程,但目的是创建或更新 Go 源文件
  • Go generate永远不会由 go build、go test 等等。它必须显式运行

  注释格式:

  在Go源文件中使用 go generate​ 命令需要在源代码中添加特殊注释,格式如下:

  其中, command​ 是要执行的命令, arguments​ 是传递给该命令的参数。

//go:generate command argument...

Generate命令的使用示例

  例如,假设我们有一个名为 hello.go​ 的源文件,其中包含以下注释:

//go:generate echo "Hello, World!"

  当我们在命令行中运行 go generate​ 命令时,它将自动执行上述注释中的命令,并在控制台中输出 "Hello, World!"。

Generate生成顺序

//go:generate echo "top"
package main

import "fmt"

//go:generate echo "middle"
func main() {
    fmt.Println("hello, go generate")
}

//go:generate echo "tail"

$go generate main.go
top
middle
tail

正则匹配生成

  go generate还可以通过-run使用正则式去匹配各源文件中go generate指示符中的命令,并仅执行匹配成功的命令:

// 未匹配到任何go generate指示符中的命令
$go generate -x -v -run "protoc" ./...
main.go
subpkg1/subpkg1.go
subpkg2/subpkg2.go

Generate应用场景

生成枚举类型的String方法

  这里我们需要安装stringer工具

 go install  golang.org/x/tools/cmd/stringer@latest

  假如有如下枚举

type BACnetComfirmedServiceChoice byte

const (
    ServiceConfirmedAcknowledgeAlarm           BACnetComfirmedServiceChoice = 0 //确认报警服务
    ServiceConfirmedComfirmedCOVNotification   BACnetComfirmedServiceChoice = 1 //证实COV通告服务
    ServiceConfirmedComfirmedEventNotification BACnetComfirmedServiceChoice = 2 //证实事件通过服务
}

  通常我们会为枚举类型手写String方法,这样在打印上面枚举常量时能输出有意义的内容:

func (d BACnetComfirmedServiceChoice ) String() string {
    switch d {
    case ServiceConfirmedAcknowledgeAlarm           :
        return "确认报警服务"
    case ServiceConfirmedComfirmedCOVNotification   :
        return "证实COV通告服务"
    case ServiceConfirmedComfirmedEventNotification:
        return "证实事件通过服务"
    }

    return "确认报警服务"
}

  如果一个项目中枚举常量类型有很多,逐个为其手写String方法费时费力。当枚举常量有变化的时候,手动维护String方法十分烦琐且易错。对于这种情况,使用go generate驱动stringer工具为这些枚举类型自动生成String方法的实现不失为一个较为理想的方案。下面就是利用go generate对上面示例的改造:

//go:generate stringer -type=BACnetComfirmedServiceChoice -linecomment
type BACnetComfirmedServiceChoice byte

const (
    ServiceConfirmedAcknowledgeAlarm           BACnetComfirmedServiceChoice = 0 //确认报警服务
    ServiceConfirmedComfirmedCOVNotification   BACnetComfirmedServiceChoice = 1 //证实COV通告服务
    ServiceConfirmedComfirmedEventNotification BACnetComfirmedServiceChoice = 2 //证实事件通过服务
}

  利用generate生成代码如下:

// Code generated by "stringer -type=BACnetComfirmedServiceChoice -linecomment"; DO NOT EDIT.

package generate

import "strconv"

func _() {
    // An "invalid array index" compiler error signifies that the constant values have changed.
    // Re-run the stringer command to generate them again.
    var x [1]struct{}
    _ = x[ServiceConfirmedAcknowledgeAlarm-0]
    _ = x[ServiceConfirmedComfirmedCOVNotification-1]
    _ = x[ServiceConfirmedComfirmedEventNotification-2]
}

const _BACnetComfirmedServiceChoice_name = "确认报警服务证实COV通告服务证实事件通过服务"

var _BACnetComfirmedServiceChoice_index = [...]uint8{0, 18, 39, 63}

func (i BACnetComfirmedServiceChoice) String() string {
    if i >= BACnetComfirmedServiceChoice(len(_BACnetComfirmedServiceChoice_index)-1) {
        return "BACnetComfirmedServiceChoice(" + strconv.FormatInt(int64(i), 10) + ")"
    }
    return _BACnetComfirmedServiceChoice_name[_BACnetComfirmedServiceChoice_index[i]:_BACnetComfirmedServiceChoice_index[i+1]]
}

protobuf

  通过generate将protoc预生成指令写在代码中

package main

import (
    "fmt"
    msg "go-generate/protobuf/msg"
)

//go:generate protoc -I ./IDL msg.proto --gofast_out=./msg
func main() {
    var m = msg.Request{
        MsgID:  "xxxx",
        Field1: "field1",
        Field2: []string{"field2-1", "field2-2"},
    }
    fmt.Println(m)
}

ebpf

  在cilium/ebpf中使用generate调用其cmd下的bpf2go进行编译

/ $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf xdp.c -- -I../headers

func main() {
    ....
}

总结

  ​go generate​ 命令是一个非常有用的工具,它可以帮助我们自动化地生成代码。在本文中,我们介绍了如何在Go源代码中使用 go generate​ 命令,并提供了一些示例来说明它的用法。使用 go generate​ 命令可以提高我们的开发效率,减少我们的重复性工作。

  ‍

参考资料

  1. 命令 go - Go 编程语言 (google.cn)
  2. Generating code - The Go Programming Language
  3. gostringer 命令 - github.com/sourcegraph/gostringer - go 包
  4. ebpf/main.go at master · cilium/ebpf (github.com)
相关文章
|
3月前
|
关系型数据库 MySQL Go
Go - 代码生成工具
Go - 代码生成工具
34 3
|
3月前
|
Shell Go
Go 语言Air 工具使用入门
在Go开发中,频繁的手动重启应用以加载新代码既耗时又低效。为此,我们引入了Air——一款专为Go项目设计的自动重载工具。Air通过监听文件变化,实现代码更改后的自动编译与运行,极大提升了开发效率。本文将指导你完成Air的安装与配置,包括如何启动Air、忽略临时文件以及理解其工作原理,让Go项目开发更加流畅高效。
|
JSON 安全 Linux
Go 语言学习之 go tool
Go 语言学习之 go tool
106 0
|
Go iOS开发 MacOS
Go学习笔记-代码调试工具 dlv
Go学习笔记-代码调试工具 dlv
946 1
Go学习笔记-代码调试工具 dlv
|
JSON 人工智能 API
用 Go 编写 ChatGPT 插件
用 Go 编写 ChatGPT 插件
53 0
|
Kubernetes Shell Go
Go编程模式 - 7-代码生成
良好的命名能体现出其价值。尤其是在错误码的处理上,无需再去查询错误码对应的错误内容,直接可以通过命名了解。
65 0
|
缓存 IDE 数据可视化
Go 日常开发常备第三方库和工具(中)
重点和大家分享下我们日常开发中所使用到的一些第三方库与工具。 这里我主要将这些库分为两类: 业务开发 基础工具开发
|
Go 开发者
[Go开源工具] go-optioner:轻松生成函数选项模式代码
你是否使用过 functional options 函数选项模式?在使用时,你是否遇到过多字段的结构体而需要手动编写大量的设置选项函数的代码? 本文介绍了 go-opioner 开源工具的安装和使用,它能够根据结构体的定义,自动生成函数选项模式的代码。
136 0
|
测试技术 程序员 Go
Go 语言快速入门指南:Go 测试
编程不易,好的程序员也不敢保证程序无 Bug。因此,软件开发过程的一个重要部分是测试。为我们的代码编写测试是确保质量和提高可靠性的好方法。
|
存储 JSON 前端开发
Go 语言快速入门指南:Go 模板介绍
Go 提供了两个非常棒的模板库—— text/template 和 html/template。这两个模板的使用方式相同,但是在区别在于:html/template 包会做一些编码来帮助防止代码注入。这种编码方式最酷的部分是它是上下文相关的,这意味着它可以发生在 HTML、CSS、JavaScript 甚至 URL 中,模板库将确定如何正确编码文本。