Golang:从字符串(函数名)获取函数指针

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

Golang: pointer to function from string (function's name)

问题

有没有办法从函数的名称(以字符串形式表示)获取函数的指针?这是为了将某些函数作为参数传递给另一个函数。你知道,一种元编程的方式。

英文:

Is there any chance to get pointer to function from function's name, presented as string? This is needed for example to send some function as argument to another function. Some sort of metaprogramming, you know.

答案1

得分: 51

Go函数是一等值。你不需要回到动态语言的技巧。

package main

import "fmt"

func someFunction1(a, b int) int {
        return a + b
}

func someFunction2(a, b int) int {
        return a - b
}

func someOtherFunction(a, b int, f func(int, int) int) int {
        return f(a, b)
}

func main() {
        fmt.Println(someOtherFunction(111, 12, someFunction1))
        fmt.Println(someOtherFunction(111, 12, someFunction2))
}

Playground


输出:

123
99

如果函数的选择取决于一些仅在运行时已知的值,你可以使用一个映射:

m := map[string]func(int, int) int {
        "someFunction1": someFunction1,
        "someFunction2": someFunction2,
}

...

z := someOtherFunction(x, y, m[key])
英文:

Go functions are first class values. You don't need to revert to the tricks from dynamic languages.

package main

import "fmt"

func someFunction1(a, b int) int {
        return a + b
}

func someFunction2(a, b int) int {
        return a - b
}

func someOtherFunction(a, b int, f func(int, int) int) int {
        return f(a, b)
}

func main() {
        fmt.Println(someOtherFunction(111, 12, someFunction1))
        fmt.Println(someOtherFunction(111, 12, someFunction2))
}

Playground


Output:

123
99

If the selection of the function depends on some run-time-only known value, you can use a map:

m := map[string]func(int, int) int {
        "someFunction1": someFunction1,
        "someFunction2": someFunction2,
}

...

z := someOtherFunction(x, y, m[key])

答案2

得分: 17

接受的答案可能是你应该做的。

这里有一种使用反射的方法,允许传递可变数量的参数。目前需要手动构建支持的函数列表(map)(参见main方法),但这可以改进。

package main

import "fmt"
import "reflect"
import "errors"

func foo() {
    fmt.Println("我们正在运行foo")
}

func bar(a, b, c int) {
    fmt.Println("我们正在运行bar", a, b, c)
}

func Call(m map[string]interface{}, name string, params ...interface{}) (result []reflect.Value, err error) {
    f := reflect.ValueOf(m[name])
    if len(params) != f.Type().NumIn() {
        err = errors.New("参数的数量不适配。")
        return
    }
    in := make([]reflect.Value, len(params))
    for k, param := range params {
        in[k] = reflect.ValueOf(param)
    }
    result = f.Call(in)
    return
}

func main() {
    // 注意:为了得到完美的分数:使用反射来构建这个map
    funcs := map[string]interface{}{
        "foo": foo,
        "bar": bar,
    }
    
    Call(funcs, "foo")
    Call(funcs, "bar", 1, 2, 3)
}

灵感/来源

英文:

The accepted answer answer is probably what you should do.

Here is an approach using reflection that allows to pass a flexible number of arguments as well. Currently it requires building a list (map) of supported functions manually (see main method), but this could be improved.

package main

import "fmt"
import "reflect"
import "errors"

func foo() {
	fmt.Println("we are running foo")
}

func bar(a, b, c int) {
	fmt.Println("we are running bar", a, b, c)
}

func Call(m map[string]interface{}, name string, params ... interface{}) (result []reflect.Value, err error) {
    f := reflect.ValueOf(m[name])
    if len(params) != f.Type().NumIn() {
        err = errors.New("The number of params is not adapted.")
        return
    }
    in := make([]reflect.Value, len(params))
    for k, param := range params {
        in[k] = reflect.ValueOf(param)
    }
    result = f.Call(in)
    return
}

func main() {
	// nota bene: for perfect score: use reflection to build this map
	funcs := map[string]interface{} {
        	"foo": foo,
        	"bar": bar,
	}
	
	Call(funcs, "foo")
	Call(funcs, "bar", 1, 2, 3)
}

Inspiration/source

答案3

得分: 4

如果函数是一个'Method',你可以使用reflect.Value.MethodByName函数。请参考这里的reflect文档

英文:

If the function is a 'Method', you can use reflect.Value.MethodByName

see reflect documentation here

答案4

得分: 3

我对你想要做的事情不是完全清楚。接受的答案应该涵盖了你尝试做的任何基础知识。

我想补充一下,crypto包有一种有趣的间接注册其他包的方式。具体请看crypto.go

基本上它的工作原理是crypto包有一个空的映射,像这样:

var regFuncs = make(map[key]func (arg) result)

其中“key”将是一个唯一的类型(int、string等),值将是你期望的函数原型。

然后一个包会使用init函数注册自己:

func init() {
    master.RegisterFunc("name", myFunc)
}

使用import _ "path/to/package"将包本身包含进来。

然后主包将有一种获取该函数的方式。

使用crypto,你可以这样使用sha 256:

crypto.SHA256.New()

但你必须首先在主包中这样导入它:

import _ "crypto/sha256"

希望这能帮到你。

英文:

I'm not entirely clear on what you're wanting to do. The accepted answer should cover the fundamentals of anything you're trying to do.

I would like to add that the crypto package has an interesting way of indirectly registering other packages. Specifically take a look at crypto.go.

Essentially how it works is the crypto package has an empty map like this:

var regFuncs = make(map[key]func (arg) result)

Where "key" would be a unique type (of int, string, etc..) and the value would be the function prototype you're expecting.

A package would then register itself using the init function:

func init() {
    master.RegisterFunc("name", myFunc)
}

The package itself would be included using import _ "path/to/package".

And then the master package would have some way of fetching that function.

With crypto, you can use sha 256 like this:

crypto.SHA256.New()

But you have to first include it in your main like this:

import _ "crypto/sha256"

Hopefully that helps.

huangapple
  • 本文由 发表于 2013年8月2日 21:22:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/18017979.html
匿名

发表评论

匿名网友

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

确定