我正在编写一个简单的应用程序,该应用程序以预定义的格式加载插件。示例插件如下:
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”函数,并尝试将其强制转换为返回函数的接口,然后调用它:
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
如果我从GetProcessor
返回一个实际实例(而不是指针),并尝试将该函数转换为返回Processor
的那个实例,我将得到相同的结果:
Error casting processor getter for plugin simple_shout: func() main.Processor
如何从插件获取结构实例(因此加载返回函数的函数)并在我的情况下断言它是预期的接口?
UPD::如果我从Processor
界面中删除了所有内容(也就是说,它只是一个空界面):
type Processor interface {}
并尝试将procGetterInter
强制转换为返回指向Processor
接口的指针的函数:
procGetter, ok := procGetterInter.(func() *Processor)
我仍然遇到相同的错误:
plugin.Symbol is func() *main.Processor, not func() *main.Processor (types from different scopes)
为什么它甚至不转换为指向空接口的指针?
答案 0 :(得分:1)
插件内的函数具有签名:
func GetProcessor() *Processor
您将此符号查找为interface{}
,然后尝试键入断言类型的值
func() interface{}
这些类型不匹配,因为这些函数类型具有不同的返回类型。 Spec: Function types:
函数类型表示所有具有相同参数和结果类型的函数集。
因此您只能键入断言相同的函数类型,但是问题是您不能引用插件中声明的标识符(函数的返回类型是插件中定义的自定义类型)。
一个简单的解决方案是将类型声明移动到另一个包,这是一个通用包,将由插件和主应用程序(加载插件)使用。
另一种解决方案是声明您的函数以返回interface{}
值,以便您可以键入断言该函数,然后可以对其进行调用,并获得类型为interface{}
的值。然后,您的主应用程序可以定义一个接口类型,该接口类型包含您感兴趣的方法,然后您可以在主应用程序中将assert键入此接口类型。
在此处查看详细信息和示例:go 1.8 plugin use custom interface
另请参阅相关问题:
Is it possible to share a custom data type between a go plugin and an application?
答案 1 :(得分:0)
TL; DR: 在此处查看完整的工作演示: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
的变量是可以的,但是类型断言失败,因为类型断言在问:这实际上是一个东西吗? 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.
好吧,在没有任何背景知识的情况下,问题在于,您从插件中查找的符号必须与类型声明所声明的类型完全相同,而不仅仅是关闭-足够允许分配。
如@icza所述,您有两种选择:
选项1:快速而肮脏,完成工作 在您的插件中
func GetGeneric() interface{} {
return &Processor{}
}
在您的主要应用中:(为清楚起见,略过错误处理)
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"))