2015-08-21 2 views
10

Насколько я знаю (см. here и here), механизм обнаружения типа не существует в reflect package, который ожидает, что у вас уже есть экземпляр типа или значения, которое вы хотите проверить.Перейти (задуматься): Как найти все типы пакетов во время выполнения?

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

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

import "time" 
import "fmt" 

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

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

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


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

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

+0

Единственное, о чем я могу думать, это создать код, который вызывает функцию register в 'init()' для вас. – THUNDERGROOVE

+0

Да, @THUNDERGROOVE, это в основном то, что я реализовал. Но я не хочу генерировать код. Я скорее запустил 'go test', и библиотека обнаружила все типы, создала их экземпляр, а затем вызовет их. Все в одном процессе. Это утомительная проблема. – mdwhatcott

ответ

1

Нет, нет.

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

2

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

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

+0

Да, я все еще могу открыть проблему, так как было бы очень приятно иметь что-то вроде 'reflect.PackageOf (« путь »). Типы()'. – mdwhatcott

9

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

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

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 с.


Edit: я тестировал и выполнен этот код успешно и имеют uploaded it as a gist.

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

+1

Это замечательный ;-) – Volker

+1

Святое собрание, Бэтмен! –

+0

@thwd: Ого, это безумие. Теперь для золотого вопроса: после вызова typelinks(), чтобы получить имена типов строк [], могу ли я как-то подключиться к пакету отражения, чтобы создать экземпляр новых экземпляров этих имен? – mdwhatcott

11

В 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 для компиляции исходного кода.

+0

Ничего себе, не знал, что эта новая функциональность существует. Какое хорошее время! Теперь для золотого вопроса: после получения pkg.Scope().Имена(), можно ли каким-либо образом подключить пакет отражения для создания экземпляров новых экземпляров имен(), которые представляют типы? – mdwhatcott

+1

Кажется, что вы ищете архитектуру плагина. Сейчас он не реализован в Go 1.5. Читайте https://docs.google.com/document/d/1nr-TQHw_er6GOQRsF6T43GGhFDelrAP0NqSS_00RgZQ/edit# для получения дополнительной информации – Alvivi

+1

Мой мозг просто взорвался. – mdwhatcott

Смежные вопросы