本文介绍了如何在运行时发现所有包类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

据我所知(请参见此处和)在反射包,它期望您已经具有要检查的类型或值的实例.

As far as I'm aware (see here, and here) there is no type discovery mechanism in the reflect package, which expects that you already have an instance of the type or value you want to inspect.

是否有任何其他方法来发现正在运行的go程序包中的所有导出类型(尤其是结构)?

Is there any other way to discover all exported types (especially the structs) in a running go package?

这就是我希望拥有的(但不存在):

Here's what I wish I had (but it doesn't exist):

import "time"
import "fmt"

func main() {
    var types []reflect.Type
    types = reflect.DiscoverTypes(time)
    fmt.Println(types)
}

最终目标是能够发现满足特定条件的包的所有结构,然后能够实例化这些结构的新实例.

The end goal is to be able to discover all the structs of a package that meet certain criteria, then be able to instantiate new instances of those structs.

BTW,用于标识类型的注册功能对于我的用例来说不是不是有效方法.

BTW, a registration function that identifies the types is not a valid approach for my use case.

无论您是否认为这是一个好主意,这就是为什么我想要这种功能(因为我知道您会问这个问题):

我已经编写了代码生成实用程序,该实用程序可以加载源文件并构建AST以进行扫描嵌入指定类型的类型.该实用程序的输出是基于发现的类型的一组go测试函数.我使用go generate调用此实用程序来创建测试功能,然后运行go test来执行生成的测试功能.每次测试更改(或添加新类型)时,我都必须在重新运行go test之前重新运行go generate.这就是注册功能不是有效选项的原因.我想避免执行go generate步骤,但这将需要我的实用程序成为由运行中的程序包导入的库.库代码需要以某种方式在init()期间扫描正在运行的名称空间,以查找嵌入预期库类型的类型.

I've written a code generation utility that loads go source files and builds an AST to scan for types that embed a specified type. The output of the utility is a set of go test functions based on the discovered types. I invoke this utility using go generate to create the test functions then run go test to execute the generated test functions. Every time the tests change (or a new type is added) I must re-run go generate before re-running go test. This is why a registration function is not a valid option. I'd like to avoid the go generate step but that would require my utility to become a library that is imported by the running package. The library code would need to somehow scan the running namespace during init() for types that embed the expected library type.

推荐答案

(请参阅底部的2019年更新)

(see bottom for 2019 update)

警告:未经测试且不可靠.每当发布新版本的Go时可能会中断.

Warning: untested and hacky. Can break whenever a new version of Go is released.

通过对Go的运行时进行一些修改,可以获得运行时所知道的所有类型.在您自己的程序包中包含一个小的程序集文件,其中包含:

It is possible to get all types the runtime knows of by hacking around Go's runtime a little. Include a small assembly file in your own package, containing:

TEXT yourpackage·typelinks(SB), NOSPLIT, $0-0
    JMP reflect·typelinks(SB)

yourpackage中,声明函数原型(无主体):

In yourpackage, declare the function prototype (without body):

func typelinks() []*typeDefDummy

在类型定义旁边:

type typeDefDummy struct {
    _      uintptr           // padding
    _      uint64            // padding
    _      [3]uintptr        // padding
    StrPtr *string
}

然后仅调用类型链接,遍历切片并读取每个StrPtr的名称.寻找以yourpackage开头的那些.请注意,如果在不同的路径中有两个名为yourpackage的软件包,则此方法将不会明确工作.

Then just call typelinks, iterate over the slice and read each StrPtr for the name. Seek those starting with yourpackage. Note that if there are two packages called yourpackage in different paths, this method won't work unambiguously.

是的,假设d是类型*typeDefDummy的值(注意星号,非常重要):

Yeah, assuming d is a value of type *typeDefDummy (note the asterisk, very important):

t := reflect.TypeOf(*(*interface{})(unsafe.Pointer(&d)))

现在t是一个reflect.Type值,可用于实例化reflect.Value s.

Now t is a reflect.Type value which you can use to instantiate reflect.Values.

我成功测试并执行了这段代码,并将其作为要点上传.

I tested and executed this code successfully and have uploaded it as a gist.

调整软件包名称,并根据需要添加路径.

Adjust package names and include paths as necessary.

自从我最初发布此答案以来,发生了很多变化.这是对如何在2019年使用Go 1.11进行相同操作的简短描述.

A lot has changed since I originally posted this answer. Here's a short description of how the same can be done with Go 1.11 in 2019.

$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

$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)

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)
        }
    }
}

黑客很开心!

这篇关于如何在运行时发现所有包类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-29 06:05
查看更多