首页 > 解决方案 > 如何从插件的主应用程序调用函数?

问题描述

我最近研究了Go 插件,而不是自己手动加载 .so 文件。

基本上,我有一个游戏服务器应用程序,我希望能够在服务器启动时加载插件(使用插件包)。然后,在插件本身中,我希望能够调用作为服务器一部分的导出函数。

假设我有这个插件,它被编译为example_plugin.so使用go build -buildmode=plugin

package main

import "fmt"

func init() {
    fmt.Println("Hello from plugin!")
}

然后说我有这个服务器应用程序,它加载插件(并最终在后台调用“init”函数):

package main

import (
    "fmt"
    "plugin"
)

func main() {
    fmt.Println("Server started")

    if _, err := plugin.Open("example_plugin.so"); err != nil {
        panic(err)
    }
}

// some API function that loaded plugins can call
func GetPlayers() {}

输出是:

Server started
Hello from plugin!

这可以按预期工作,但是我希望能够GetPlayers从插件(和任何其他插件)调用该函数(以及服务器应用程序中的任何其他导出函数,理想情况下)。我正在考虑制作某种由接口组成的库包含服务器实现的 API 函数,但是我不知道从哪里开始。我知道我可能需要使用 .a 文件或类似文件。

为了澄清起见,我正在开发此应用程序以在 Linux 上使用,因此我可以使用仅适用于 Linux 的解决方案。

抱歉,如果措辞不佳,这是第一次在 SO 上发帖。

标签: go

解决方案


如评论中所述,有一个 Lookup 功能。在模块的文档中,他们有以下示例:

// A Symbol is a pointer to a variable or function.
// For example, a plugin defined as
//
// var V int
//
// func F() { fmt.Printf("Hello, number %d\n", V) }
//
// may be loaded with the Open function and then the exported package
// symbols V and F can be accessed

package main

import (
    "fmt"
    "plugin"
)

func main() {
    p, err := plugin.Open("plugin_name.so")
    if err != nil {
        panic(err)
    }
    v, err := p.Lookup("V")
    if err != nil {
        panic(err)
    }
    f, err := p.Lookup("F")
    if err != nil {
        panic(err)
    }
    *v.(*int) = 7
    f.(func())() // prints "Hello, number 7"
}

我认为这里最令人困惑的台词是

*v.(*int) = 7
f.(func())() // prints "Hello, number 7"

他们中的第一个执行类型断言来*int断言确实v指向的指针int。这是必要的,因为Lookup返回一个interface{}并且为了对值做任何有用的事情,你应该澄清它的类型。

第二行执行另一个类型断言,这一次确保它f是一个没有参数且没有返回值的函数,然后立即调用它。由于F原始模块中的函数正在引用V(我们已将其替换为 7),因此此调用将显示Hello, number 7.


推荐阅读