Golang 插件类型断言
Golang plugin type assertion
我正在编写一个以预定义格式加载插件的简单应用程序。示例插件如下:
package main
import (
"errors"
"fmt"
"strings"
)
var (
ok bool
InvConfig = errors.New("invalid config")
)
type Processor struct {
logEverything bool
}
func (p *Processor) Init(config map[string]interface{}) error {
p.logEverything, ok = config["log_everything"].(bool)
if !ok {
return InvConfig
}
return nil
}
func (p *Processor) Process(buf []byte) []byte {
if p.logEverything {
fmt.Printf("Shouter got data: %v\n", buf)
}
return []byte(strings.ToUpper(string(buf)))
}
func GetProcessor() *Processor {
return &Processor{}
}
我不太明白如何在我的主程序中加载这样的结构。所以,我声明了一个接口:
type Processor interface {
Init(map[string]interface{}) error
Process(buf []byte) []byte
}
然后我加载 "getter" 函数并尝试将其转换为函数 returning 接口然后调用它:
p, err := plugin.Open(filepath)
if err != nil {
logrus.Fatalf("Error opening plugin %s: %v", pluginName, err)
}
procGetterInter, err := p.Lookup("GetProcessor")
if err != nil {
logrus.Fatalf("Error loading processor getter for plugin %s: %v", pluginName, err)
}
procGetter, ok := procGetterInter.(func() interface{})
if !ok {
logrus.Fatalf("Error casting processor getter for plugin %s: %T", pluginName, procGetterInter)
}
但是转换失败并出现错误:
Error casting processor getter for plugin simple_shout: func() *main.Processor
如果我 return 来自 GetProcessor
的实际实例(不是指针)并尝试将函数转换为 returning Processor
,我得到同样的结果:
Error casting processor getter for plugin simple_shout: func() main.Processor
如何从插件中获取结构实例(因此加载函数 returning 它)并类型断言它是我的预期接口?
更新: 如果我从 Processor
界面中删除所有内容(也就是说,它变成一个空界面):
type Processor interface {}
并尝试将 procGetterInter
转换为函数 return 指向 Processor
接口的指针:
procGetter, ok := procGetterInter.(func() *Processor)
我仍然得到同样的错误:
plugin.Symbol is func() *main.Processor, not func() *main.Processor (types from different scopes)
为什么它甚至不转换为指向空接口的指针?
插件内的函数有一个签名:
func GetProcessor() *Processor
您将此符号查找为 interface{}
并尝试 type-assert 类型为
的值
func() interface{}
这些类型不匹配,因为这些函数类型具有不同的 return 类型。 Spec: Function types:
A function type denotes the set of all functions with the same parameter and result types.
所以你可能只能键入assert相同的函数类型,但问题是你不能引用插件中声明的标识符(函数的return类型是插件中定义的自定义类型) .
所以一个简单的解决方案是将类型声明移动到另一个包,一个插件和主应用程序(加载插件)都将使用的通用包。
另一个解决方案是将你的函数声明为return一个interface{}
值,这样你就可以键入断言这个函数,你可以调用它,你将获得一个[=12]类型的值=].然后您的主应用程序可能会定义一个接口类型,其中包含您感兴趣的方法,并且在主应用程序中您可以为该接口类型键入断言。
在此处查看详细信息和示例:go 1.8 plugin use custom interface
另见相关问题:
长话短说:
在此处查看完整的工作演示:https://github.com/jvmatl/go-plugindemo
冗长但(希望!)内容丰富的答案:
插件在几个方面都很棘手,@icza 的回答是完全正确的,但是要理解为什么它是正确的以及它如何适用于你的问题,你需要明白 go 接口的灵活性并不适用于复杂的类型。
您可能已经 运行 在其他上下文中了解过此内容:
这在 Go 中是合法的:
var a interface{}
var b int
a = b // yep, an int meets the spec for interface{} !
但这不是:
var aa []interface{}
var bb []int
aa = bb // cannot use bb (type []int) as type []interface {} in assignment
同样,对于函数,这是合法的:
type Runner interface {
Run()
}
type UsainBolt struct{}
func (ub *UsainBolt) Run() {
fmt.Println("Catch me if you can!")
}
var a Runner
var b *UsainBolt
a = b // Yep, a (pointer to) Usain Bolt is a runner!
但这不是:
var aa func() Runner
var bb func() *UsainBolt
aa = bb // cannot use bb (type func() *UsainBolt) as type func() Runner in assignment
现在让我们看看定义的函数类型。这是真正有趣的地方:
type RunnerGetter func() Runner
var rg RunnerGetter
rg = getUsain // <-- Nope: doesn't compile: "cannot use getUsain (type func() *UsainBolt) as type RunnerGetter in assignment"
rg = getRunner // <-- This *assignment* is allowed: getRunner is assignable to a type RunnerGetter
var i interface{} = getRunner
rg = i.(RunnerGetter) // compiles, but panics at runtime: "interface conversion: interface {} is func() main.Runner, not main.RunnerGetter"
换句话说,语言可以将 func getRunner() Runner
分配给 RunnerGetter
类型的变量,但类型断言失败,因为类型断言要求:is这个东西居然是一个RunnerGetter类型的变量?答案是否定的,它是一个接近的 func() Runner
,但不太正确,所以我们恐慌。
但这行得通:
var rg RunnerGetter
var i interface{}
i = rg // after this assignment, i *is* a RunnerGetter
rg = i.(RunnerGetter) // so this assertion passes.
好的,在所有这些背景都没有的情况下,问题是您从插件中查找的符号必须完全与您的类型断言所说的类型相同, 不仅仅是 close-enough-to-allow-assignment.
如@icza 所述,您有两种选择:
选项 1:快速而简单,完成工作
在你的插件中
func GetGeneric() interface{} {
return &Processor{}
}
在您的主要内容中:(error-handling 为清楚起见跳过)
p, _ := plugin.Open(pluginFile) // load plugin
newIntf, _ := p.Lookup("Getgeneric") // find symbol
newProc, _ := newIntf.(func() interface{}) // assert symbol to generic constructor
shoutProc, _ := newProc().(processors.Processor) // call generic constructor, type assert the return value
// Now use your new plugin!
shoutProc.Init(map[string]interface{}{"log_everything": true})
output := shoutProc.Process([]byte("whisper"))
选项 2:更干净,如果你有很多插件则更好
在另一个包中声明所有插件必须满足的接口:
package processors
// Every plugin must be able to give me something that meets this interface
type Processor interface {
Init(map[string]interface{}) error
Process(buf []byte) []byte
}
在您的插件中:
type ShoutProcessor struct {
configured bool
logEverything bool
}
func NewProcessor() processors.Processor {
return &ShoutProcessor{}
}
在你的主要:
p, _ := plugin.Open(pluginFile) // load plugin
newProcIntf, _ := p.Lookup("NewProcessor") // lookup constructor
newProc, _ := newProcIntf.(func() processors.Processor) // assert the type of the func
shoutProc := newProc() // call the constructor, get a new ShoutProcessor
// ready to rock and roll!
shoutProc.Init(map[string]interface{}{"log_everything": true})
output := shoutProc.Process([]byte("whisper"))
我正在编写一个以预定义格式加载插件的简单应用程序。示例插件如下:
package main
import (
"errors"
"fmt"
"strings"
)
var (
ok bool
InvConfig = errors.New("invalid config")
)
type Processor struct {
logEverything bool
}
func (p *Processor) Init(config map[string]interface{}) error {
p.logEverything, ok = config["log_everything"].(bool)
if !ok {
return InvConfig
}
return nil
}
func (p *Processor) Process(buf []byte) []byte {
if p.logEverything {
fmt.Printf("Shouter got data: %v\n", buf)
}
return []byte(strings.ToUpper(string(buf)))
}
func GetProcessor() *Processor {
return &Processor{}
}
我不太明白如何在我的主程序中加载这样的结构。所以,我声明了一个接口:
type Processor interface {
Init(map[string]interface{}) error
Process(buf []byte) []byte
}
然后我加载 "getter" 函数并尝试将其转换为函数 returning 接口然后调用它:
p, err := plugin.Open(filepath)
if err != nil {
logrus.Fatalf("Error opening plugin %s: %v", pluginName, err)
}
procGetterInter, err := p.Lookup("GetProcessor")
if err != nil {
logrus.Fatalf("Error loading processor getter for plugin %s: %v", pluginName, err)
}
procGetter, ok := procGetterInter.(func() interface{})
if !ok {
logrus.Fatalf("Error casting processor getter for plugin %s: %T", pluginName, procGetterInter)
}
但是转换失败并出现错误:
Error casting processor getter for plugin simple_shout: func() *main.Processor
如果我 return 来自 GetProcessor
的实际实例(不是指针)并尝试将函数转换为 returning Processor
,我得到同样的结果:
Error casting processor getter for plugin simple_shout: func() main.Processor
如何从插件中获取结构实例(因此加载函数 returning 它)并类型断言它是我的预期接口?
更新: 如果我从 Processor
界面中删除所有内容(也就是说,它变成一个空界面):
type Processor interface {}
并尝试将 procGetterInter
转换为函数 return 指向 Processor
接口的指针:
procGetter, ok := procGetterInter.(func() *Processor)
我仍然得到同样的错误:
plugin.Symbol is func() *main.Processor, not func() *main.Processor (types from different scopes)
为什么它甚至不转换为指向空接口的指针?
插件内的函数有一个签名:
func GetProcessor() *Processor
您将此符号查找为 interface{}
并尝试 type-assert 类型为
func() interface{}
这些类型不匹配,因为这些函数类型具有不同的 return 类型。 Spec: Function types:
A function type denotes the set of all functions with the same parameter and result types.
所以你可能只能键入assert相同的函数类型,但问题是你不能引用插件中声明的标识符(函数的return类型是插件中定义的自定义类型) .
所以一个简单的解决方案是将类型声明移动到另一个包,一个插件和主应用程序(加载插件)都将使用的通用包。
另一个解决方案是将你的函数声明为return一个interface{}
值,这样你就可以键入断言这个函数,你可以调用它,你将获得一个[=12]类型的值=].然后您的主应用程序可能会定义一个接口类型,其中包含您感兴趣的方法,并且在主应用程序中您可以为该接口类型键入断言。
在此处查看详细信息和示例:go 1.8 plugin use custom interface
另见相关问题:
长话短说: 在此处查看完整的工作演示:https://github.com/jvmatl/go-plugindemo
冗长但(希望!)内容丰富的答案:
插件在几个方面都很棘手,@icza 的回答是完全正确的,但是要理解为什么它是正确的以及它如何适用于你的问题,你需要明白 go 接口的灵活性并不适用于复杂的类型。
您可能已经 运行 在其他上下文中了解过此内容:
这在 Go 中是合法的:
var a interface{}
var b int
a = b // yep, an int meets the spec for interface{} !
但这不是:
var aa []interface{}
var bb []int
aa = bb // cannot use bb (type []int) as type []interface {} in assignment
同样,对于函数,这是合法的:
type Runner interface {
Run()
}
type UsainBolt struct{}
func (ub *UsainBolt) Run() {
fmt.Println("Catch me if you can!")
}
var a Runner
var b *UsainBolt
a = b // Yep, a (pointer to) Usain Bolt is a runner!
但这不是:
var aa func() Runner
var bb func() *UsainBolt
aa = bb // cannot use bb (type func() *UsainBolt) as type func() Runner in assignment
现在让我们看看定义的函数类型。这是真正有趣的地方:
type RunnerGetter func() Runner
var rg RunnerGetter
rg = getUsain // <-- Nope: doesn't compile: "cannot use getUsain (type func() *UsainBolt) as type RunnerGetter in assignment"
rg = getRunner // <-- This *assignment* is allowed: getRunner is assignable to a type RunnerGetter
var i interface{} = getRunner
rg = i.(RunnerGetter) // compiles, but panics at runtime: "interface conversion: interface {} is func() main.Runner, not main.RunnerGetter"
换句话说,语言可以将 func getRunner() Runner
分配给 RunnerGetter
类型的变量,但类型断言失败,因为类型断言要求:is这个东西居然是一个RunnerGetter类型的变量?答案是否定的,它是一个接近的 func() Runner
,但不太正确,所以我们恐慌。
但这行得通:
var rg RunnerGetter
var i interface{}
i = rg // after this assignment, i *is* a RunnerGetter
rg = i.(RunnerGetter) // so this assertion passes.
好的,在所有这些背景都没有的情况下,问题是您从插件中查找的符号必须完全与您的类型断言所说的类型相同, 不仅仅是 close-enough-to-allow-assignment.
如@icza 所述,您有两种选择:
选项 1:快速而简单,完成工作 在你的插件中
func GetGeneric() interface{} {
return &Processor{}
}
在您的主要内容中:(error-handling 为清楚起见跳过)
p, _ := plugin.Open(pluginFile) // load plugin
newIntf, _ := p.Lookup("Getgeneric") // find symbol
newProc, _ := newIntf.(func() interface{}) // assert symbol to generic constructor
shoutProc, _ := newProc().(processors.Processor) // call generic constructor, type assert the return value
// Now use your new plugin!
shoutProc.Init(map[string]interface{}{"log_everything": true})
output := shoutProc.Process([]byte("whisper"))
选项 2:更干净,如果你有很多插件则更好 在另一个包中声明所有插件必须满足的接口:
package processors
// Every plugin must be able to give me something that meets this interface
type Processor interface {
Init(map[string]interface{}) error
Process(buf []byte) []byte
}
在您的插件中:
type ShoutProcessor struct {
configured bool
logEverything bool
}
func NewProcessor() processors.Processor {
return &ShoutProcessor{}
}
在你的主要:
p, _ := plugin.Open(pluginFile) // load plugin
newProcIntf, _ := p.Lookup("NewProcessor") // lookup constructor
newProc, _ := newProcIntf.(func() processors.Processor) // assert the type of the func
shoutProc := newProc() // call the constructor, get a new ShoutProcessor
// ready to rock and roll!
shoutProc.Init(map[string]interface{}{"log_everything": true})
output := shoutProc.Process([]byte("whisper"))