The Go source code reading series are my source code reading notes. Because the version of Go on my computer is 1.13.4, I chose this version as the learning version. For this reason, I Fork the Go source code on Github and created the study1.13.4 branch to record my personal understanding of the source code or Chinese comments. After reading a package, a summary will be made, just like this is a summary of the flag package. Of course, in the process of organizing, I found the Go Night Reading series of videos , which also benefited me a lot.
Introduction
The flag package is a package used to parse command line parameters in Go. Why choose it as the first package to read, because it has a small amount of code. Its core code is only a flag.go file less than 1000.
File structure
The file structure of the flag package is very simple, just one level. There are 5 files in a folder, the files and their functions are as follows:
-
flag.go
The core package of flag implements all the functions of command line parameter analysis
-
export_test.go
A practical tool for testing, which defines all the basic variables and functions needed for testing
-
flag_test.go
The flag test file contains 17 test units
-
example_test.go
The sample file of flag, which introduces three common usage examples of the flag package
-
example_value_test.go
The sample file of flag, which introduces a more complex example
Run test
Let me first introduce the operating environment of Go.
# 通过 brew install go 安装,源码位置为 $GOROOT/src
GOROOT=/usr/local/opt/go/libexec
# 阅读的源码通过 go get -v -d github.com/haojunyu/go 下载,源码位置为 $GOPATH/src/github.com
GOPATH=$HOME/go
Test the pits stepped on by the flag package separately:
- It is not possible to test against a single file, it needs to be tested against a package.
Here is an important point to talk about the export_test.go file, which is part of the flag package package flag
, but it does exist specifically for testing. To put it bluntly, it is only a ResetForTesting
method to clear all command parameter states and directly set the Usage function. This method will be frequently used in test cases. So running the following command alone will report an error "flag_test.go:30:2: undefined: ResetForTesting"
# 测试当前目录(报错)
go test -v .
# 测试包
go test -v flag
go test -v flag
The source code is tested$GOROOT/src
under the (In my current test environment)
After specifying the flag package, source code is actually running $GOROOT
under, and this should be my installation a relationship.
to sum up
Interface conversion can achieve functions similar to templates in C++
A structure type is defined in the flag package Flag
, which is used to store a command parameter, which is defined as follows.
// A Flag represents the state of a flag.
// 结构体Flag表示一个参数的所有信息,包括名称,帮助信息,实际值和默认值
type Flag struct {
Name string // name as it appears on command line名称
Usage string // help message帮助信息
Value Value // value as set实现了取值/赋值方法的接口
DefValue string // default value (as text); for usage message默认值
}
Wherein the parameter is a value of the command Value
interface type, which is defined as follows:
// Set is called once, in command line order, for each flag present.
// The flag package may call the String method with a zero-valued receiver,
// such as a nil pointer.
// 接口Value是个接口,在结构体Flag中用来存储每个参数的动态值(参数类型格式各样)
type Value interface {
String() string // 取值方法
Set(string) error // 赋值方法
}
Why do you do this? Because this can achieve the function similar to the template. Any type T
as long as the realization of the Value
interfaces in the String
and Set
methods, this type T
of variable v
can be converted to Value
the interface type and use String
to value, use Set
to assign. This can perfectly solve the purpose of using the same code operation for different types, and has the same effect as the template in C++.
Function vs method
Functions and methods are a set of statements that perform a task together. The difference between the two is that the caller is different. The caller of the function is the package, and the caller of the method is the receiver. The flag of the source code, there are too many functions there is only one line, is to use variable bag CommandLine
, calls the same method.
// Parsed reports whether f.Parse has been called.
// Parsed方法: 命令行参数是否已经解析
func (f *FlagSet) Parsed() bool {
return f.parsed
}
// Parsed reports whether the command-line flags have been parsed.
func Parsed() bool {
return CommandLine.Parsed()
}
new
vs make
new
And make
is the Go language of two memory allocation primitives. The things they do and the types they target are different.
new
Similar to the keyword function in other programming languages, both apply for a memory space to the system to store the corresponding type of data, but there are some differences, the difference is that it will zero the space in the slice. That new(T)
will be based on the type of T
the heap apply a zero memory space, and returns a pointer *T
.
make
For the slice, mapping and only three types of data channel T
construction, and the return type is T
an initialized value (not zero). The reason is that these three data types are reference data types and must be initialized before use. Just like a slice is a descriptor with three items, including a pointer to an array, length and capacity. By make
creating a corresponding type of process is to assign variable is a space, and then to create a corresponding type variables according to the corresponding descriptors. About make
the details of the written draveness you can see the design and implementation of the Go language .
// Bool defines a bool flag with specified name, default value, and usage string.
// The return value is the address of a bool variable that stores the value of the flag.
func (f *FlagSet) Bool(name string, value bool, usage string) *bool {
p := new(bool)
f.BoolVar(p, name, value, usage)
return p
}
// sortFlags returns the flags as a slice in lexicographical sorted order.
// sortFlags函数:按字典顺序排序命令参数,并返回Flag的切片
func sortFlags(flags map[string]*Flag) []*Flag {
result := make([]*Flag, len(flags))
i := 0
for _, f := range flags {
result[i] = f
i++
}
sort.Slice(result, func(i, j int) bool {
return result[i].Name < result[j].Name
})
return result
}
Assign pointer to interface variable
Go interface has two meanings, the first layer is a set of methods (functions not) signature, it requires the recipient (the particular type T
or particular types of pointers *T
) to implementation details; another is a type, which type can Accept all the recipients who should accept reality. For an in-depth understanding of the concept of interfaces, you can read the interface of Go language design and implementation . Flag in the package StringVar
method newStringValue(value, p)
returns *stringValue
the type, and the type (the recipient) implements Value
an interface ( String
and Set
methods), then it can be assigned to the type of Value
interface variables.
// StringVar defines a string flag with specified name, default value, and usage string.
// The argument p points to a string variable in which to store the value of the flag.
// StringVar方法:将命令行参数的默认值value赋值给变量*p,并生成结构Flag并置于接受者中f.formal
func (f *FlagSet) StringVar(p *string, name string, value string, usage string) {
f.Var(newStringValue(value, p), name, usage) // newStringValue返回值是*stringValue类型,之所以能赋值给Value接口是因为newStringValue实现Value接口时定义的接受者为*stringValue
}
There are flag_test
packages in the flag folder
flag folder has a flag_test
package, because the folder that contains the core code flag.go and test code * _test.go. These two parts of code are not distinguished by folders. So the flag_test
meaning of packages is that the test code and distinguish the core code. When the package is referenced, only the core code is used.
// example_test.go
package flag_test
Scope
There are detailed introductions about the scope of Golang variable scope and Go language Bible about scope . The former is easier to understand and the latter is more professional. Flag in the package TestUsage
test sample, as func(){called=true}
a function of the TestUsage
defined functions, and directly as a parameter passed to the ResetForTesting
function, so the function is a local variable and called
is at the same level, of course, to the variable assignment in the function it is reasonable .
// called变量的作用域
func TestUsage(t *testing.T) {
called := false
// 变量called的作用域
ResetForTesting(func() {
called = true })
if CommandLine.Parse([]string{
"-x"}) == nil {
t.Error("parse did not fail for unknown flag")
} else {
t.Error("hahahh")
}
if !called {
t.Error("did not call Usage for unknown flag")
}
}
Follow-up in-depth TODO
- go test test principle
- Interface conversion principle
- reflection
references
- Go night reading flag package video
- Memory allocation for effective Go programming
- Go language design and implementation of make and new
- Novice tutorial's Go language variable scope
- Scope in the Go Bible
- Comparison of value receiver and pointer receiver in Go language
- Go CodeReviewComments
- Golang variable scope
- Scope in the Go Bible
- Interface of Go language design and implementation
If this article has helped you, or if you are interested in technical articles, you can follow the WeChat public account: Technical Tea Party, you can receive related technical articles as soon as possible, thank you!
This article is automatically published by ArtiPub , a multi- posting platform