如何从插件中调用主应用程序的函数?

huangapple go评论103阅读模式
英文:

How do I call a function from the main application from a plugin?

问题

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

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

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

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

// 加载的插件可以调用的一些API函数
func GetPlayers() {}

输出结果为:

Server started
Hello from plugin!

这个结果符合预期,但是我希望能够从插件(以及其他插件)中调用GetPlayers函数(理想情况下,还包括服务器应用程序中的其他导出函数)。我在考虑创建一个包含服务器实现的API函数的接口库,但是我不知道从何开始。我知道我可能需要使用一个.a文件或类似的东西。

为了澄清,我正在为在Linux上使用开发此应用程序,所以我可以接受仅在Linux上工作的解决方案。

如果表达不清楚,我在SO上发帖是第一次,还请见谅。

英文:

I have recently looked into Go plugins instead of manually loading .so files myself.

Basically, I have a game server application, and I want to be able to load plugins (using plugins package) when the server starts. Then, in the plugin itself, I want to be able to call exported functions that are a part of the server.

Say I have this plugin, which is compiled to example_plugin.so using go build -buildmode=plugin:

package main

import "fmt"

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

Then say I have this server application, which loads the plugin (and ultimately calls the "init" function under the hood):

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

The output is:

Server started
Hello from plugin!

This works as expected, however I want to be able to call that GetPlayers function (and any other exported functions in the server application, ideally) from the plugin (and any other plugins.) I was thinking about making some sort of library consisting of interfaces containing API functions that the server implements, however I have no idea where to start. I know I will probably need to use a .a file or something similar.

For clarification, I am developing this application for use on Linux, so I am fine with a solution that only works on Linux.

Apologies if this is poorly worded, first time posting on SO.

答案1

得分: 0

如评论中所提到的,有一个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"

第一行代码对v进行了类型断言,将其断言为*int类型,以确保v确实是指向int的指针。这是必要的,因为Lookup函数返回的是一个interface{}类型的值,为了对值进行有用的操作,你需要明确其类型。

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

英文:

As mentioned in the comments, there is a Lookup function. In the documentation for the module they have the following example:

// 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"
}

I think the most confusing lines here are

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

The first one of them performs a type assertion to *int to assert that v is indeed a pointer to int. That is needed since Lookup returns an interface{} and in order to do anything useful with a value, you should clarify its type.

The second line performs another type assertion, this time making sure that f is a function with no arguments and no return values, after which, immediately calls it. Since function F from the original module was referencing V (which we've replaced with 7), this call will display Hello, number 7.

huangapple
  • 本文由 发表于 2021年5月25日 19:03:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/67686654.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定