Перейти (задуматься): Как найти все типы пакетов во время выполнения?

Насколько мне известно (см. здесь и здесь), не обнаружено типа механизм в отражает пакет, который ожидает, что у вас уже есть экземпляр типа или значения, которое вы хотите проверить.

Есть ли другой способ обнаружить все экспортированные типы (особенно структуры) в запущенном пакете go?

Вот что я хотел бы иметь (но его не существует):

import "time"
import "fmt"

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

Конечная цель состоит в том, чтобы иметь возможность обнаруживать все структуры пакета, соответствующие определенным критериям, а затем создавать экземпляры этих структур.

BTW, функция регистрации, которая идентифицирует типы, не действительный подход для моего использования.


Думаете ли вы, что это хорошая идея или нет, вот почему я хочу эту возможность (потому что знаю, что вы спросите):

Я написал утилиту генерации кода, которая загружает исходные файлы и создает AST для сканирования типов, которые встраивают указанный тип. Вывод утилиты представляет собой набор тестовых функций go на основе обнаруженных типов. Я вызываю эту утилиту с помощью go generate для создания тестовых функций, затем запускаю go test для выполнения сгенерированных тестовых функций. Каждый раз, когда тесты меняются (или добавляется новый тип), я должен повторно запускать сгенерировать перед повторным запуском go test. Вот почему функция регистрации не является допустимым вариантом. Я бы хотел избежать шага go generate, но это потребует от моей утилиты стать библиотекой, которая импортируется запущенным пакетом. Библиотечный код должен каким-то образом отсканировать запущенное пространство имен во время init() для типов, которые встраивают ожидаемый тип библиотеки.

Ответ 1

В Go 1.5 вы можете использовать новый пакет types и importer для проверки двоичных и исходных пакетов. Например:

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

Вы можете использовать пакет go/build, чтобы извлечь все установленные пакеты. Или вы можете настроить импортер Lookup для проверки двоичных файлов вне среды.

До 1,5 единственным способом без взлома является использование пакета ast для компиляции источника код.

Ответ 2

Предупреждение: непроверенный и взломанный. Может прерываться всякий раз, когда выпущена новая версия Go.

Можно получить все типы, о которых знает среда выполнения, немного взломав Run. Включите небольшой файл сборки в свой собственный пакет, содержащий:

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

В yourpackage, объявите прототип функции (без тела):

func typelinks() []*typeDefDummy

Наряду с определением типа:

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

Затем просто вызовите typelinks, перейдите по срезу и прочитайте каждый StrPtr для имени. Ищите тех, которые начинаются с yourpackage. Обратите внимание: если в разных путях есть два пакета с именем yourpackage, этот метод не будет работать однозначно.

Можно ли каким-либо образом подключить пакет отражения, чтобы создать новые экземпляры этих имен?

Да, если d - значение типа *typeDefDummy (обратите внимание на звездочку, очень важно):

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

Теперь t - это значение reflect.Type, которое вы можете использовать для создания экземпляра reflect.Value s.


Изменить: я успешно протестировал и выполнил этот код и загрузил его как сущность.

Отрегулируйте имена пакетов и при необходимости включите пути.

Ответ 3

К сожалению, я не думаю, что это возможно. Пакеты не "действуют" в Go, вы не можете "вызывать функцию" на нем. Вы также не можете вызывать функцию в типе, но вы можете вызвать reflect.TypeOf в экземпляре типа и получить reflect.Type, который является абстрактной абстракцией типа. Просто нет такого механизма для пакетов, нет reflect.Package.

С учетом сказанного вы можете указать проблему об отсутствии (и практичности добавления) reflect.PackageOf и т.д.

Ответ 4

Нет. Нет.

Если вы хотите "знать" свои типы, вам придется их зарегистрировать.