Flag package for Go source code reading

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:

  1. 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 ResetForTestingmethod 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
  1. go test -v flagThe source code is tested $GOROOT/srcunder the (In my current test environment)

After specifying the flag package, source code is actually running $GOROOTunder, 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 Valueinterface 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 Tas long as the realization of the Valueinterfaces in the Stringand Setmethods, this type Tof variable vcan be converted to Valuethe interface type and use Stringto value, use Setto 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

newAnd makeis the Go language of two memory allocation primitives. The things they do and the types they target are different.
newSimilar 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 Tthe heap apply a zero memory space, and returns a pointer *T.
makeFor the slice, mapping and only three types of data channel Tconstruction, and the return type is Tan 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 makecreating 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 makethe 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 Tor 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 StringVarmethod newStringValue(value, p)returns *stringValuethe type, and the type (the recipient) implements Valuean interface ( Stringand Setmethods), then it can be assigned to the type of Valueinterface 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_testpackages in the flag folder

flag folder has a flag_testpackage, 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_testmeaning 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 TestUsagetest sample, as func(){called=true}a function of the TestUsagedefined functions, and directly as a parameter passed to the ResetForTestingfunction, so the function is a local variable and calledis 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

  1. Go night reading flag package video
  2. Memory allocation for effective Go programming
  3. Go language design and implementation of make and new
  4. Novice tutorial's Go language variable scope
  5. Scope in the Go Bible
  6. Comparison of value receiver and pointer receiver in Go language
  7. Go CodeReviewComments
  8. Golang variable scope
  9. Scope in the Go Bible
  10. 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!
Technical tea party

This article is automatically published by ArtiPub , a multi- posting platform

Guess you like

Origin blog.csdn.net/haojunyu2012/article/details/112913087
Recommended