插件符号作为函数 return

Plugin symbol as function return

我 运行 陷入了一种我不理解的 Go 行为。我的想法是导入一个插件,它实现了两个包之外的接口。如果一个结构被 returned 它工作正常,但为了确保它实现了接口,我想 return 一个失败的接口。

接口定义:

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)

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 函数。

函数 returning testpl 打印预期输出:

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

有人可以解释为什么这不可能吗?如果在这种情况下不允许您构建插件,那么创建插件不是更容易吗?

你想要的是可能的,但后台有一些东西阻止它工作。

这就是你要从插件中查找一个名为Greeter变量Plugin.Lookup() 将 return 指向此变量的指针!如果不行,你只能检查它的值,而不能改变它。

您可以通过简单地打印存储在 sym 中的值的类型来验证这一点:

fmt.Printf("%T\n", sym)

在您的第一个案例中 func getPlugin() testpl,输出将是:

*main.testpl

在第二种情况下 func getPlugin() iface.IPlugin,输出将是:

*iface.IPlugin

(是的,它是一个接口指针!)

在第三种情况下 func getPlugin() interface{},输出将是:

*interface {}

所以你的第一个例子是有效的,因为存储在 sym 中的值是类型 *main.testpl,它也实现了 iface.IPlugin(因为 main.testpl 实现了它,所以指针类型)。

回到你的第二个例子:func getPlugin() iface.IPlugin

存储在sym中的值是*iface.IPlugin类型。指向接口的指针类型的值从不满足任何接口(空接口除外),因此尝试从 *iface.IPlugin 类型的值 type-assert iface.IPlugin 永远不会成功。您必须键入 assert *iface.IPlugin 类型,之后您可以取消引用以获得类型 iface.IPlugin 的值。它可能看起来像这样:

pgPtr, ok := sym.(*iface.IPlugin)
if !ok {
    panic(errors.New("error binding plugin to interface"))
}
pg := *pgPtr

现在一切正常!

为避免此类麻烦和混乱,您可以实现您的插件以公开一个 函数,其中 return 是您 Greeter:

func Greeter() iface.IPlugin { return testpl{} }

然后当然要去掉 Greeter 全局变量。如果这样做,您可以查找 Greeter 类型的符号:

func() iface.IPlugin

不同之处在于查找函数不需要 plugin 包到 return 指向值的指针,而在变量的情况下它需要。一个简单的函数类型,没有pointer-to-interface-kung-fu。使用它来获取欢迎程序将是:

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

查看相关/类似问题:go 1.8 plugin use custom interface