dongnuan7956 2015-08-21 03:48
浏览 69
已采纳

如何在运行时发现所有包类型?

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.

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, a registration function that identifies the types is not a valid approach for my use case.


Whether you think it's a good idea or not, here's why I want this capability (because I know you're going to ask):

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.

  • 写回答

4条回答 默认 最新

  • dongying2112 2015-08-21 08:41
    关注

    (see bottom for 2019 update)

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

    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)
    

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

    func typelinks() []*typeDefDummy
    

    Alongside a type definition:

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

    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.

    can I somehow hook into the reflect package to instantiate new instances of those names?

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

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

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


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

    Adjust package names and include paths as necessary.

    Update 2019

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

    Happy hacking!

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

悬赏问题

  • ¥15 想问一下树莓派接上显示屏后出现如图所示画面,是什么问题导致的
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)
  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号