Plugin symbol as function return

泄露秘密 提交于 2019-11-29 15:27:33

What you want is possible, but there is something in the background that prevents it from working.

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.

You can verify this by simply printing the type of the value stored in sym:

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

In your first case func getPlugin() testpl, output will be:

*main.testpl

In your second case func getPlugin() iface.IPlugin, output will be:

*iface.IPlugin

(Yes, it's a pointer to an interface!)

In your third case func getPlugin() interface{}, output will be:

*interface {}

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

Back to your 2nd example: func getPlugin() 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!

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{} }

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

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

See related / similar question: go 1.8 plugin use custom interface

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!