jason 075d766964 first | 3 năm trước cách đây | |
---|---|---|
.. | ||
examples | 3 năm trước cách đây | |
generic | 3 năm trước cách đây | |
out | 3 năm trước cách đây | |
parse | 3 năm trước cách đây | |
.gitignore | 3 năm trước cách đây | |
.travis.yml | 3 năm trước cách đây | |
LICENSE | 3 năm trước cách đây | |
README.md | 3 năm trước cách đây | |
doc.go | 3 năm trước cách đây | |
main.go | 3 năm trước cách đây | |
main_test.go | 3 năm trước cách đây |
Install:
go get github.com/cheekybits/genny
=====
(pron. Jenny) by Mat Ryer (@matryer) and Tyler Bunnell (@TylerJBunnell).
Until the Go core team include support for generics in Go, genny
is a code-generation generics solution. It allows you write normal buildable and testable Go code which, when processed by the genny gen
tool, will replace the generics with specific types.
stdin
and stdout
or specify in and out filesBUILTINS
and NUMBERS
wildtype to generate specific code for all built-in (and number) Go typesWe have started building a library of common things, and you can use genny get
to generate the specific versions you need.
For example: genny get maps/concurrentmap.go "KeyType=BUILTINS ValueType=BUILTINS"
will print out generated code for all types for a concurrent map. Any file in the library may be generated locally in this way using all the same options given to genny gen
.
genny [{flags}] gen "{types}"
gen - generates type specific code from generic code.
get <package/file> - fetch a generic template from the online library and gen it.
{flags} - (optional) Command line flags (see below)
{types} - (required) Specific types for each generic type in the source
{types} format: {generic}={specific}[,another][ {generic2}={specific2}]
Examples:
Generic=Specific
Generic1=Specific1 Generic2=Specific2
Generic1=Specific1,Specific2 Generic2=Specific3,Specific4
Flags:
-in="": file to parse instead of stdin
-out="": file to save output to instead of stdout
-pkg="": package name for generated files
-in
- specify the input file (rather than using stdin)-out
- specify the output file (rather than using stdout)To use Go 1.4's go generate
capability, insert the following comment in your source code file:
//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "KeyType=string,int ValueType=string,int"
//go:generate
-in
and -out
flags to specify the files to work ongenny
command as usual after the flagsNow, running go generate
(in a shell) for the package will cause the generic versions of the files to be generated.
go generate
many times$GOFILE
to refer to the current file//go:generate
line will be removed from the outputTo see a real example of how to use genny
with go generate
, look in the example/go-generate directory.
Define your generic types using the special generic.Type
placeholder type:
type KeyType generic.Type
type ValueType generic.Type
Then write the generic code referencing the types as your normally would:
func SetValueTypeForKeyType(key KeyType, value ValueType) { /* ... */ }
Since generic.Type
is a real Go type, your code will compile, and you can even write unit tests against your generic code.
Pass the file through the genny gen
tool with the specific types as the argument:
cat generic.go | genny gen "KeyType=string ValueType=interface{}"
The output will be the complete Go source file with the generic types replaced with the types specified in the arguments.
Given this generic Go code which compiles and is tested:
package queue
import "github.com/cheekybits/genny/generic"
// NOTE: this is how easy it is to define a generic type
type Something generic.Type
// SomethingQueue is a queue of Somethings.
type SomethingQueue struct {
items []Something
}
func NewSomethingQueue() *SomethingQueue {
return &SomethingQueue{items: make([]Something, 0)}
}
func (q *SomethingQueue) Push(item Something) {
q.items = append(q.items, item)
}
func (q *SomethingQueue) Pop() Something {
item := q.items[0]
q.items = q.items[1:]
return item
}
When genny gen
is invoked like this:
cat source.go | genny gen "Something=string"
It outputs:
// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
package queue
// StringQueue is a queue of Strings.
type StringQueue struct {
items []string
}
func NewStringQueue() *StringQueue {
return &StringQueue{items: make([]string, 0)}
}
func (q *StringQueue) Push(item string) {
q.items = append(q.items, item)
}
func (q *StringQueue) Pop() string {
item := q.items[0]
q.items = q.items[1:]
return item
}
To get a something for every built-in Go type plus one of your own types, you could run:
cat source.go | genny gen "Something=BUILTINS,*MyType"
Check out the test code files for more real examples.
Once you have defined a generic type with some code worth testing:
package slice
import (
"log"
"reflect"
"github.com/stretchr/gogen/generic"
)
type MyType generic.Type
func EnsureMyTypeSlice(objectOrSlice interface{}) []MyType {
log.Printf("%v", reflect.TypeOf(objectOrSlice))
switch obj := objectOrSlice.(type) {
case []MyType:
log.Println(" returning it untouched")
return obj
case MyType:
log.Println(" wrapping in slice")
return []MyType{obj}
default:
panic("ensure slice needs MyType or []MyType")
}
}
You can treat it like any normal Go type in your test code:
func TestEnsureMyTypeSlice(t *testing.T) {
myType := new(MyType)
slice := EnsureMyTypeSlice(myType)
if assert.NotNil(t, slice) {
assert.Equal(t, slice[0], myType)
}
slice = EnsureMyTypeSlice(slice)
log.Printf("%#v", slice[0])
if assert.NotNil(t, slice) {
assert.Equal(t, slice[0], myType)
}
}
generic.Type
isBecause generic.Type
is an empty interface type (literally interface{}
) every other type will be considered to be a generic.Type
if you are switching on the type of an object. Of course, once the specific versions are generated, this issue goes away but it's worth knowing when you are writing your tests against generic code.