Detailed go generate

From time to time, I will see "go generate" in other people's code, and I generally know what it does, but I usually write business code without paying much attention to this knowledge. I'm free today and study a little bit.

The purpose of go generate

Go generate is commonly used to automatically generate code. It is one of the official tools of golang tools. It has been supported since go1.4 and provides convenience for developers. You can read the blog written by Rob to understand its entry: https://blog.golang.org/generate .

Common usage of go generate

Let's explain the usage of generate according to the example of Rob's blog. For example, the following code:

package pill
type Pill int
const (
	Placebo Pill = iota
	Aspirin
	Ibuprofen
	Paracetamol
	Acetaminophen = Paracetamol
)

func GetPill(p Pill) string {
	switch p {
	case Placebo:
		return "Placebo"
	case Aspirin:
		return "Aspirin"
	case Ibuprofen:
		return "Ibuprofen"
	case Paracetamol:
		return "Paracetamol"
	}
	return ""
}

Let's write another test to see:

func TestGetPill(t *testing.T) {
	fmt.Println(GetPill(Pill(1))) //output: Aspirin
}

There are some constants of enumeration type, and we want these constants to return their corresponding names. We need to write a GetPill method similar to this. But there is a problem. Once there are more and more pill, then our case will be more and more, whether it is addition, deletion and modification is a trouble, then you can use generate combined with another tool stringer to complete the automatic code generation .
Since stringer is not in the tool set of the official release, we need to install it ourselves and execute:

$ go get golang.org/x/tools/cmd/stringer

Next, we modify the code and add a line of comments:

//go:generate stringer -type=Pill
package pill
type Pill int
const (
	Placebo Pill = iota
	Aspirin
	Ibuprofen
	Paracetamol
	Acetaminophen = Paracetamol
)

GetPill () can be removed, then we execute:

$ go generate

In the directory at the same level, the file of pile_string.go is generated, and the content of the file is as follows:

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

package pill

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[Placebo-0]
	_ = x[Aspirin-1]
	_ = x[Ibuprofen-2]
	_ = x[Paracetamol-3]
}

const _Pill_name = "PlaceboAspirinIbuprofenParacetamol"

var _Pill_index = [...]uint8{0, 7, 14, 23, 34}

func (i Pill) String() string {
	if i < 0 || i >= Pill(len(_Pill_index)-1) {
		return "Pill(" + strconv.FormatInt(int64(i), 10) + ")"
	}
	return _Pill_name[_Pill_index[i]:_Pill_index[i+1]]
}

The generated code looks a bit strange, but we do n’t need to pay attention to it in actual use. The code looks strange in order to reduce the memory footprint. Looking back, let's take a look at what our needs are. We want to get the return information of each enumeration value, and then write a test to try:

func TestGetPill2(t *testing.T) {
	fmt.Println(Pill(1).String()) //output: Aspirin
}

As you can see, we have fulfilled our needs, and we do not need to maintain a GetPill () method.

go generate analysis

We have implemented a small example, now let's understand why this is so. Focus on the comment:

//go:generate stringer -type=Pill

First of all, when we want to execute the go generate command, there must be a go generate comment in the code.

go generate [-run regexp] [-n] [-v] [-x] [command] [build flags] [file.go... | packages]
  • -run regular expression matches the command line, only execute the matched command;
  • -v output the processed package name and source file name;
  • -n Show not to execute the command;
  • -x display and execute commands;
  • command can be any command in the environment variable PATH.

When executing the go generate command, you can also use some environment variables, as follows:

  • $ GOARCH system architecture (arm, amd64, etc.);
  • $ GOOS current OS environment (linux, windows, etc.);
  • $ GOFILE The file name currently being processed;
  • $ GOLINE The line number of the current command in the file;
  • $ GOPACKAGE The package name of the currently processed file.

Pay attention to the following points:

  • This special comment must be in the .go source file;
  • Each source file can contain multiple generate special comments;
  • When the go generate command is run, the command following the special comment will be executed;
  • When the go generate command fails, it will terminate the program;
  • Special comments must start with // go: generate, and there is no space after the double slash.

Then look at the usage in our example, after go generate, the code is actually generated, and the execution is "stringer -type = Pill". Earlier, we have go get to install stringer into our $ GOPATH / bin, and $ GOPATH / bin has been added to my environment variable PATH, so the code is generated. In the same way, other commands can be added after go generate to automatically generate code. The more common scenarios are, for example, code generation based on .proto files, similar to the following:

//go generate protoc --go_out=plugins=grpc:. server.proto

Go generate can have multiple definitions in the file, you can write multiple generate comments, and you can also write aliases for commands, such as:

//go:generate -command yacc go tool yacc
//go:generate yacc -o gopher.go gopher.y

Here yacc after -command is an alias of "go tool yacc", the actual execution of the go generate command is equivalent to the execution:

$ go tool yacc -o gopher.go gopher.y

The method of using go generate in the code is roughly as described above, but in actual compilation, executing go build does not execute go generate and needs to be executed separately. This can be solved by writing Makefile and other automatic compilation methods.

Some tools commonly used by go generate

In the process of learning go generate, I also saw a wiki of common tools for generating. I have not used them all. I will share with you here, hoping to improve development efficiency, https://github.com/golang/ go / wiki / GoGenerateTools .

go generateOnly useful if you have the tools to use it! This is an incomplete list of useful tools for generating code.

  • goyacc – Go的Yacc。
  • stringer -implements an fmt.Stringerenumeration interface.
  • gostringer  –  fmt.GoStringerimplements an interface for enumeration.
  • jsonenums  -Enumeration implementation json.Marshalerand json.Unmarshalerinterface.
  • go-syncmap -Generate Go code using a general template as a software package sync.Map.
  • go-syncpool – Generating Go code using a general template as a software package sync.Pool.
  • go-atomicvalue – Generate Go code using a general template as a software package atomic.Value.
  • go-nulljson – Use the package as database/sql.Scannera general template to implement sum to generate Go code database/sql/driver.Valuer.
  • go-enum – Generate Go code using a package as a general template for implementing interfaces fmt.Stringerbinaryjsontextsqlyamlenumeration.
  • go-import -Perform automatic import of non-go files.
  • gojson -Generate go structure definition from sample json document.
  • vfsgen -generates a vfsdata.go file that statically implements a given virtual file system.
  • goreuse -Use the package as a generic template to generate Go code by replacing definitions.
  • embedfiles -embed files in Go code.
  • ragel -state machine compiler
  • peachpy -x86-64 assembler embedded in Python to generate Go assembly
  • bundle – Bundle creates a single source file version suitable for source packages contained in a specific target package.
  • msgp – MessagePack's Go code generator
  • protobuf - protobuf
  • thriftrw – thrift
  • gogen-euro - euro
  • swagger-gen-types – generate code from swagger definition
  • avo -use Go to generate assembly code
  • Wire – Go's compile-time dependency injection
  • sumgen – generate interface method implementation from sum-type declaration
  • interface-extractor -generate the required type of interface, only use the method in the package.
  • deep-copy – Create a deep copy method for a given type.

Welcome to pay attention to my public number: onepunchgo, leave me a message.

image

Published 20 original articles · Likes0 · Visits 760

Guess you like

Origin blog.csdn.net/qq_31362439/article/details/105163641