英文:
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
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论