插件符号作为函数返回 [英] Plugin symbol as function return
问题描述
我遇到了我不了解的Go行为.我的想法是导入一个插件,该插件实现了两个软件包都没有的接口.如果返回一个struct,它可以正常工作,但是要确保它实现了该接口,我想返回一个失败的接口.
I'm running into a Go behaviour which I don't understand. My idea is to import a plugin which implements an interface that is out of both packages. If a struct is returned it works fine, but to be sure it implements the interface, I want to return an interface which fails.
接口定义:
package iface
type IPlugin interface{
SayHello(string)
SayGoodby(string)
WhatsYourName() string
}
主程序如下:
package main
import (
"plugin"
"plugin_test/iface"
"errors"
"fmt"
)
//go:generate go build -buildmode=plugin -o ./pg/test.so ./pg/test.go
func main(){
path := "pg/test.so"
plug, err := plugin.Open(path)
if err != nil {
panic(err)
}
sym, err := plug.Lookup("Greeter")
if err != nil {
panic(err)
}
var pg iface.IPlugin
pg, ok := sym.(iface.IPlugin)
if !ok {
panic(errors.New("error binding plugin to interface"))
}
fmt.Printf("You're now connected to: %s \n", pg.WhatsYourName())
pg.SayHello("user")
pg.SayGoodby("user")
}
插件(存储在pg/test.go中)
The plugin (stored in pg/test.go)
package main
import (
"fmt"
"plugin_test/iface"
)
type testpl struct {}
func(pl testpl) SayHello(s string){
fmt.Printf("Plugin says hello to %s \n", s)
}
func(pl testpl) SayGoodby(s string){
fmt.Printf("Plugin says goodby to %s \n", s)
}
func(pl testpl) WhatsYourName() string{
return "my name is Test-Plugin"
}
/* This function works */
func getPlugin() testpl{
return testpl{}
}
/* This function doesn't work */
func getPlugin() iface.IPlugin{
return testpl{}
}
/* This function also doesn't work */
func getPlugin() interface{}{
return testpl{}
}
var Greeter = getPlugin()
我自己尝试了每个getPlugin
函数.
I tried every getPlugin
function on its own.
返回testpl
的函数将输出预期的输出:
The function returning testpl
prints the expected output:
You're now connected to: my name is Test-Plugin
Plugin says hello to user
Plugin says goodby to user
其他功能在sym.(iface.IPlugin)
panic: error binding plugin to interface
goroutine 1 [running]:
main.main()
/home/../../../main.go:27 +0x343
exit status 2
有人可以解释为什么这不可能吗?如果在这种情况下不允许您构建插件,创建插件会不会更容易?
Can someone explain why this isn't possible? Wouldn't it be easier to create a plugin if it did't let you build your plugin in such a case?
推荐答案
您想要的东西是可能的,但是在后台有一些东西使它无法正常工作.
What you want is possible, but there is something in the background that prevents it from working.
这就是您要从插件中查找名为Greeter
的变量. Plugin.Lookup()
将返回指向该变量的指针!如果不能,则只能检查其值,而不能更改它.
This is namely that you want to lookup a variable named Greeter
from the plugin. Plugin.Lookup()
will return a pointer to this variable! If it wouldn't, you could only inspect its value, but you couldn't change it.
您可以通过简单地打印存储在sym
中的值的类型来验证这一点:
You can verify this by simply printing the type of the value stored in sym
:
fmt.Printf("%T\n", sym)
在您的第一种情况func getPlugin() testpl
中,输出将为:
In your first case func getPlugin() testpl
, output will be:
*main.testpl
在第二种情况下func getPlugin() iface.IPlugin
,输出将是:
In your second case func getPlugin() iface.IPlugin
, output will be:
*iface.IPlugin
(是的,它是指向接口的指针!)
(Yes, it's a pointer to an interface!)
在您的第三种情况下,func getPlugin() interface{}
的输出将是:
In your third case func getPlugin() interface{}
, output will be:
*interface {}
所以第一个示例有效,因为存储在sym
中的值的类型为*main.testpl
,它也实现了iface.IPlugin
(因为main.testpl
实现了它,所以指针类型也实现了).
So your first example works because the value stored in sym
is of type *main.testpl
, which also implements iface.IPlugin
(because main.testpl
implements it, so does the pointer type).
回到您的第二个示例:func getPlugin() iface.IPlugin
Back to your 2nd example: func getPlugin() iface.IPlugin
sym
中存储的值是*iface.IPlugin
类型.指向接口的指针类型的值永远不会满足任何接口(空接口除外),因此尝试从类型*iface.IPlugin
的值进行类型断言iface.IPlugin
永远不会成功.您必须键入assert *iface.IPlugin
类型,之后可以取消引用以获取类型为iface.IPlugin
的值.看起来可能像这样:
The value stored in sym
is of type *iface.IPlugin
. A value of pointer type to interface never satisfies any interfaces (except the empty interface), so attempting to type-assert iface.IPlugin
from a value of type *iface.IPlugin
will never succeed. You have to type assert *iface.IPlugin
type, which you can dereference after to obtain a value of type iface.IPlugin
. It could look like this:
pgPtr, ok := sym.(*iface.IPlugin)
if !ok {
panic(errors.New("error binding plugin to interface"))
}
pg := *pgPtr
现在一切都按预期进行!
And now everything works as expected!
为避免此类麻烦和混乱,您可以实施插件来公开函数,该函数将为您返回Greeter
:
To avoid such hassle and confusion, you may implement your plugin to expose a function which returns you the Greeter
:
func Greeter() iface.IPlugin { return testpl{} }
然后当然要摆脱Greeter
全局变量.如果执行此操作,则可以查找Greeter
符号,其类型为:
And then get rid of the Greeter
global var of course. If you do this, you may lookup the Greeter
symbol which will be of type:
func() iface.IPlugin
区别在于查找函数不需要plugin
包返回指向该值的指针,而对于变量则需要.一种简单的函数类型,没有指向接口的功夫指针.用它来获得迎宾员将是:
The difference is that looking up a function does not require the plugin
package to return a pointer to the value, while in case of a variable it does. A simple function type, no pointer-to-interface-kung-fu. Using it to obtain the greeter would be:
Greeter, err := p.Lookup("Greeter")
if err != nil {
panic(err)
}
greeterFunc, ok := GetFilter.(func() iface.IPlugin)
if !ok {
panic(errors.New("not of expected type"))
}
greeter := greeterFunc()
// And using it:
fmt.Printf("You're now connected to: %s \n", greeter.WhatsYourName())
greeter.SayHello("user")
greeter.SayGoodby("user")
查看相关/类似问题:转到1.8插件使用自定义界面
See related / similar question: go 1.8 plugin use custom interface
这篇关于插件符号作为函数返回的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!