据我所知(参见此处和此处),反射包中没有类型发现机制,期望您已经拥有要检查的类型或值的实例.
有没有其他方法可以在run go包中发现所有导出的类型(特别是结构)?
这是我希望的(但它不存在):
import "time"
import "fmt"
func main() {
var types []reflect.Type
types = reflect.DiscoverTypes(time)
fmt.Println(types)
}
Run Code Online (Sandbox Code Playgroud)
最终目标是能够发现满足特定条件的包的所有结构,然后能够实例化这些结构的新实例.
顺便说一句,标识类型的注册函数不是我的用例的有效方法.
无论你认为这是一个好主意,这就是我想要这种能力的原因(因为我知道你会问):
我编写了一个代码生成实用程序,它可以加载源文件并构建一个AST来扫描嵌入指定类型的类型.该实用程序的输出是基于发现的类型的一组go测试函数.我调用此实用程序go generate来创建测试函数然后运行go test以执行生成的测试函数.每次测试更改(或添加新类型)时,我必须在重新运行之前重新运行go generate go test.这就是注册功能不是有效选项的原因.我想避免这go generate一步,但这需要我的实用程序成为一个由正在运行的包导入的库.init()对于嵌入了预期库类型的类型,库代码需要以某种方式扫描正在运行的命名空间.
Alv*_*ivi 24
在Go 1.5中,您可以使用新的包类型和导入程序来检查二进制包和源包.例如:
package main
import (
"fmt"
"go/importer"
)
func main() {
pkg, err := importer.Default().Import("time")
if err != nil {
fmt.Printf("error: %s\n", err.Error())
return
}
for _, declName := range pkg.Scope().Names() {
fmt.Println(declName)
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用go/build包来解压缩所有已安装的软件包.或者,您可以将Lookup导入程序配置为检查环境外的二进制文件.
在1.5之前,唯一没有hacky的方法是使用包ast来编译源代码.
thw*_*hwd 12
(见2019年更新的底部)
警告:未经测试和hacky.每当发布新版Go时都会中断.
通过对Go的运行时进行一些攻击,可以获得运行时所知道的所有类型.在您自己的包中包含一个小的汇编文件,其中包含:
TEXT yourpackage·typelinks(SB), NOSPLIT, $0-0
JMP reflect·typelinks(SB)
Run Code Online (Sandbox Code Playgroud)
在yourpackage,声明函数原型(没有正文):
func typelinks() []*typeDefDummy
Run Code Online (Sandbox Code Playgroud)
除了类型定义:
type typeDefDummy struct {
_ uintptr // padding
_ uint64 // padding
_ [3]uintptr // padding
StrPtr *string
}
Run Code Online (Sandbox Code Playgroud)
然后只需调用typelinks,遍历切片并读取每个StrPtr的名称.寻找那些以yourpackage.请注意,如果yourpackage在不同的路径中调用了两个包,则此方法将无法明确地工作.
我可以以某种方式挂钩到反射包来实例化这些名称的新实例吗?
是的,假设d是一个类型的值*typeDefDummy(注意星号,非常重要):
t := reflect.TypeOf(*(*interface{})(unsafe.Pointer(&d)))
Run Code Online (Sandbox Code Playgroud)
现在t是一个reflect.Type可用于实例化reflect.Values的值.
编辑:我成功测试并执行了此代码,并将其作为要点上传.
根据需要调整包名称并包含路径.
自从我最初发布这个答案以来,已经发生了很多变 以下是2019年Go 1.11如何实现同样的简短描述.
$GOPATH/src/tl/tl.go
package tl
import (
"unsafe"
)
func Typelinks() (sections []unsafe.Pointer, offset [][]int32) {
return typelinks()
}
func typelinks() (sections []unsafe.Pointer, offset [][]int32)
func Add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer {
return add(p, x, whySafe)
}
func add(p unsafe.Pointer, x uintptr, whySafe string) unsafe.Pointer
Run Code Online (Sandbox Code Playgroud)
$GOPATH/src/tl/tl.s
TEXT tl·typelinks(SB), $0-0
JMP reflect·typelinks(SB)
TEXT tl·add(SB), $0-0
JMP reflect·add(SB)
Run Code Online (Sandbox Code Playgroud)
main.go
package main
import (
"fmt"
"reflect"
"tl"
"unsafe"
)
func main() {
sections, offsets := tl.Typelinks()
for i, base := range sections {
for _, offset := range offsets[i] {
typeAddr := tl.Add(base, uintptr(offset), "")
typ := reflect.TypeOf(*(*interface{})(unsafe.Pointer(&typeAddr)))
fmt.Println(typ)
}
}
}
Run Code Online (Sandbox Code Playgroud)
快乐的黑客!