插件符号作为函数返回 [英] Plugin symbol as function return

查看:89
本文介绍了插件符号作为函数返回的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了我不了解的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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆