在golang中获取一个包导出函数列表

8

假设我有一个包

// ./somepkg/someFile.go
package somepkg
import "fmt"
func AnExportedFunc(someArg string) {
    fmt.Println("Hello world!")
    fmt.Println(someArg)
)

我从我的主要go文件中导入它

// ./main.go
package main
import (
    "./somepkg" // Let's just pretend I have the full path written out
    "fmt"
)

func main() {
    fmt.Println("I want to get a list of exported funcs from package 'somefolder'")
}

有没有一种方法可以访问包“somepkg”中导出的函数,然后调用它们?所有函数的参数数量/类型将在“somepkg”中保持一致。我已经查看了反射包,但不确定是否可以在不知道除包名称外的任何信息的情况下获取列表并调用函数。也许我在godocs中漏掉了某些内容,所以任何建议都将不胜感激。我的目标是创建一个系统,使人们可以将.go文件作为“插件”添加到其中。这些“插件”将具有单个导出函数,主程序本身将使用一致的参数数量和类型来调用该函数。由于对代码库的访问受到限制,因此不存在由贡献者执行任意代码的安全问题。
注意:这是编译后的代码,因此没有运行时限制。
如果用Python编写,我要做的就是这样的。
# test.py
def abc():
    print "I'm abc"

def cba():
    print "I'm cba"

并且

# foo.py
import test
flist = filter(lambda fname: fname[0] != "_", dir(test))

# Let's forget for a moment how eval() is terrible
for fname in flist:
    eval("test."+fname+"()")

运行foo.py会返回:
I'm abc
I'm cba

在golang中是否可能实现这一点?

编辑:

我应该指出,我已经通过与http://mikespook.com/2012/07/function-call-by-name-in-golang/非常相似的东西来“完成”了这个问题,但需要将每个附加的“插件”添加其导出函数到包全局映射中。虽然这“可行”,但感觉很不专业(就像整个程序一样...lol;),而且希望如果可以不需要插件编写人员进行任何额外的工作来完成它。基本上我想使它尽可能地“即插即用”。


你无法在运行时获取导出的符号,因为这些符号仅在编译时可用。如果函数未被使用,则在链接期间将其删除。(在当前的gc实现中是这样的) - JimB
我在运行时不需要它们,因为我将把“插件”编译到主程序中。我意识到称它们为“插件”有点奇怪,因此加上引号。我主要通过ast、token、parser和reflect包进行了检索,感觉可以实现我想要的,但不太确定具体如何实现。 - user1522031
1
如果你想在编译时做一些事情,反射包是无法帮助你的。你需要解析源代码并在编译之前为每个插件生成所需的代码。 - JimB
2个回答

2
正如你所猜测的那样,在Go语言中实现动态调用是困难的,如果不是不可能的话,因为Go是一种编译型语言。遍历一个包以获取导出函数列表只能让你得到函数列表。这个Playground提供了一个示例。这是一种AST(抽象语法树)方法,这意味着动态调用函数不可能,或者需要太多的变通方法。在这里,将函数名称字符串解析为函数类型是行不通的。作为替代方案,您可以通过将导出函数绑定到某些类型来尝试使用方法。
type Task struct {}
func (Task) Process0()
func (Task) Process1(v int)
func (Task) Process2(v float64)
func (Task) Process3(v1 bool, v2 string)

这完全改变了我们的操作方式,因为这四种方法现在与Task类型相关联,我们可以传递Task的空实例来调用定义在其上的方法。这可能看起来只是另一种解决方法,但在像Go这样的语言中非常常见。这在Playground中有一个示例,它实际上按预期工作。
在这两个示例中,我在playground中使用了多个文件。如果您对此结构不熟悉,只需在本地创建您的工作区并从playground复制每个文件名下的代码即可。
<Your project>
├── go.mod
├── main.go
└── task
    └── task.go

参考文献:

  1. 如何在Golang中动态调用结构体的所有方法?[重复]
  2. 如何检查函数参数和类型?[重复]
  3. 如何列出Golang包的公共方法?[重复]
  4. 抽象语法树 - 维基百科
  5. Go中的函数与方法的区别

0

最简单的方法是使用模板库来解析您的代码,并在适当的位置插入新的包名称。

Playground

您可以通过加载所有决赛并调用用户包以及将生成的文件输出到执行目录来使用它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接