生成模板代碼實現Golang泛型

生成模板代碼實現Golang泛型

Golang是強類型靜態語言,暫不支持泛型。那麼如何定義通用類型的數據結構和算法呢?使用interface{}不是好的解決方案,接口需要轉換同時會丟失靜態類型的優勢。本文我們介紹模板代碼結合代碼生成器,實現編譯時檢查、安全的、高性能的泛型編程。

1. 實現通用Set數據結構

如何實現一個數據結構(算法也一樣)支持常用類型,生成特定類型實現,可以輕鬆重用。

使用genny,非常簡單幾個步驟:

  1. 導入genny/generic
  2. 定義類型爲通用類型generic.Type,例如,命令爲ItemType
  3. 在代碼中使用這些通用類型
  4. 運行genny生成類型實現

下面實現比較簡單的Set數據結構,定義ItemSet泛型Set,同時定義了Add和Clean方法:

//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "Item=string,int"

package set

import "github.com/cheekybits/genny/generic"

// Item the type of the Set
type Item generic.Type

// ItemSet the set of Items
type ItemSet struct {
	items map[Item]bool
}

func (s *ItemSet)Add(t Item) ItemSet  {
	if s.items == nil {
		s.items = make(map[Item]bool)
	}

	_, ok := s.items[t]
	if !ok {
		s.items[t] = true
	}

	return *s
}

func (s *ItemSet) Clean(){
	s.items = make(map[Item]bool)
}

當然需要先使用go get -u github.com/cheekybits/genny 命令安裝genny,編譯通過後。運行命令:
genny -in set.go -out gen-set.go gen "Item=string,int"

命令執行之後,如果輸入文件爲set.go,則會生成gen-set.go

// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny

// Package Set creates a StringSet data structure for the string type
package set

// StringSet the set of Strings
type StringSet struct {
	items map[string]bool
}

// Add adds a new element to the Set. Returns the Set.
func (s *StringSet) Add(t string) StringSet {
	s.items[t] = true
	return *s
}

// Clear removes all elements from the Set
func (s *StringSet) Clear() {
	(*s).items = make(map[string]bool)
}

// Delete removes the string from the Set and returns Has(string)
func (s *StringSet) Delete(item string) bool {
	ret := (*s).Has(item)
	if ret {
		delete((*s).items, item)
	}
	return ret
}

// Has returns true if the Set contains the string
func (s *StringSet) Has(item string) bool {
	return (*s).items[item]
}

// Strings returns the string(s) stored
func (s *StringSet) Strings() []string {
	items := []string{}
	for i := range s.items {
		items = append(items, i)
	}
	return items
}

// Package Set creates a IntSet data structure for the int type

// IntSet the set of Ints
type IntSet struct {
	items map[int]bool
}

// Add adds a new element to the Set. Returns the Set.
func (s *IntSet) Add(t int) IntSet {
	s.items[t] = true
	return *s
}

// Clear removes all elements from the Set
func (s *IntSet) Clear() {
	(*s).items = make(map[int]bool)
}

// Delete removes the int from the Set and returns Has(int)
func (s *IntSet) Delete(item int) bool {
	ret := (*s).Has(item)
	if ret {
		delete((*s).items, item)
	}
	return ret
}

// Has returns true if the Set contains the int
func (s *IntSet) Has(item int) bool {
	return (*s).items[item]
}

// Ints returns the int(s) stored
func (s *IntSet) Ints() []int {
	items := []int{}
	for i := range s.items {
		items = append(items, i)
	}
	return items
}

我們看到genny幫我們從ItemSet **生成了StringSetIntSet結構,因爲Item=string,int

同時也生成對應結構體方法,如果你想增加其他類型,只要編輯命令重新運行。

2. 使用 go generate

我們也可以通過在文件的頂部,包文檔註釋的下面增加go generate註釋:

//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "Item=string,int"

命令指定使用genny,同時需要翻譯Item爲string和int。接着直接在源文件目錄下運行下面命令:

go generate

同樣會生成gen-set.go文件,包括string和int類型結構體。

重要:上面通用實現的數據結構可以像go generate生成的具體實現一樣,進行編譯和測試,因此可以針對通用或特定類型的實現運行測試。

3. 總結

本文介紹了利用gennygo generate生成代碼,輔助實現泛型數據結構和算法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章