28

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.

4 Answers 4

51

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])
Sign up to request clarification or add additional context in comments.

11 Comments

@SergeyGerasimov: If you want to pass a function by name ("I need exactly function name") then use the function's identifier. If you want to pass it as a string ("foo"), pass the function's identifier in quotation marks. Both techniques are shown above. Or I didn't get what you mean, perhaps?
I think he means that he needs to, given a string representation of a function name, get (and presumably eventually call) a pointer to the named function. I assume that you'd need to use the reflect package in some way.
@joshlf13 mapping a string function name to function pointer is shown in the answer, so I'm now confused even more ;-)
@zzzz I don't think you get the idea of this question. Let's say for example you have an http server waiting for client request, then you decide which backend service to call depending on the value you get from the url. The url is string. I don't want to maintain a growing map, instead I just want to call a function using the function name as a string.
'poor tricks from dynamic languages'... Have you every tried to use a function as part of a map key in Go? Probably not. Go is a wonderful language but some things are just awkward like calling something 'first class' which is not even 'comparable'. Actually, I wish they were first class values for real.
|
17

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

1 Comment

I really like this answer, it's been really helpful for a project I am working on. The one question I have, is, how do I get the return value from a function that is run this way? The only return I get is the return type and not the actual value
4

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

see reflect documentation here

4 Comments

An example would be great to show how the reflect package can be used in this situation.
How to do it when the function is not a method ?
@hunter_tech what do you mean function is not a method?
@YoviPrasetyo, not a method function of structure.
3

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.

1 Comment

Wow, thanks! Actually I understand that tricks from dynamic languages doesn't work in Go, and I definitely should rethink architecture of the this problem. Anyway, thank you very much!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.