Golang: указатель на функцию из строки (имя функции)

Есть ли шанс получить указатель на функцию из имени функции, представленной в виде строки? Это необходимо, например, для отправки какой-либо функции в качестве аргумента другой функции. Знаете, какое-то метапрограммирование.

Ответы

Ответ 1

Функции 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))
}

площадка


Выход:

123
99

Если выбор функции зависит от некоторого известного значения времени выполнения, вы можете использовать карту:

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

...

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

Ответ 2

Принятый ответ - это, вероятно, то, что вам следует делать.

Вот подход, использующий рефлексию, который также позволяет передавать гибкое количество аргументов. В настоящее время требуется создание списка (карты) поддерживаемых функций вручную (см. метод main), но это можно улучшить.

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

Вдохновение/источник

Ответ 3

Я не совсем понимаю, что вы хотите сделать. Принятый ответ должен охватывать основы всего, что вы пытаетесь сделать.

Я хотел бы добавить, что пакет crypto имеет интересный способ косвенной регистрации других пакетов. В частности, посмотрите на crypto.go.

По сути, как это работает, пакет crypto имеет пустую карту, подобную этой:

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

Где "ключ" - это уникальный тип (типа int, string и т.д.), а значение - ожидаемый прототип функции.

Пакет затем зарегистрируется, используя функцию init:

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

Сам пакет будет включен с использованием import _ "path/to/package".

И тогда у мастер-пакета будет какой-то способ извлечь эту функцию.

С crypto вы можете использовать sha 256 следующим образом:

crypto.SHA256.New()

Но сначала вы должны включить его в свой основной файл следующим образом:

import _ "crypto/sha256"

Надеюсь, это поможет.