Как мне написать прокси-функцию, которая принимает интерфейс {}, который преобразуется в функцию (… x) … y, привязывая результат к значению указателя?

#go

#Вперед

Вопрос:

Я пытаюсь создать функцию, которая действует как прокси для выполнения функции.

Он принимает указатель на значение (в котором будет задан результат), функцию и ряд параметров. Он выполняет функцию и применяет результат к значению.

Моя первая задача — выполнить функцию, поскольку объявленный тип не является func (и не может быть). думаю, я смогу достичь этого с помощью reflect.MakeFunc , но у меня нет успеха.

Ниже приведен пример того, чего я пытаюсь достичь.

 package main

import "fmt"

// Execute the function which returns this struct
type item struct {
    key        string
    value      string
    otherValue string
}

func todo(param string) (*item, error) {
    return amp;item{
        key: param,
    }, nil
}

type BindFunc struct {
    Params  func(params ...interface{}) *BindFunc
    Results func(results ...interface{}) *BindFunc
    Do      func()
}

// NewBindFunc is a proxy function should run the function and bind the results
// to the result
func NewBindFunc(fn interface{}) *BindFunc {
    b := amp;BindFunc{}

    b.Params = func(params ...interface{}) *BindFunc {
        return b
    }

    b.Results = func(results ...interface{}) *BindFunc {
        return b
    }

    b.Do = func() {
        // execute the function
    }

    return b
}

func main() {
    var result item
    var err error

    NewBindFunc(todo).
        Params("Param").
        Results(amp;result, amp;err).
        Do()

    fmt.Println(result.key)
}
  

Ответ №1:

Используйте Ценность.Вызов для вызова функции. Упростите код, используя методы на BindFunc вместо полей с функциями.

 type BindFunc struct {
    params  []interface{}
    results []interface{}
    fn      interface{}
}

func (bf *BindFunc) Params(params ...interface{}) *BindFunc {
    bf.params = params
    return bf
}

func (bf *BindFunc) Results(results ...interface{}) *BindFunc {
    bf.results = results
    return bf
}

func (bf *BindFunc) Do() {
    // Copy params to slice of reflect values.
    var params []reflect.Value
    for _, p := range bf.params {
        params = append(params, reflect.ValueOf(p))
    }

    // Invoke the function.
    results := reflect.ValueOf(bf.fn).Call(params)

    // Copy the results.
    for i, r := range results {
        reflect.ValueOf(bf.results[i]).Elem().Set(r)
    }
}

// NewBindFunc is a proxy function should run the function and bind the results
// to the result
func NewBindFunc(fn interface{}) *BindFunc {
    return amp;BindFunc{fn: fn}
}
  

Назовите это так:

 var result *item
var err error

NewBindFunc(todo).
    Params("Param").
    Results(amp;result, amp;err).
    Do()
  

Запустите его на игровой площадке.

Приведенный выше код будет паниковать, если есть несоответствие в типах, несоответствие в количестве аргументов или несоответствие в количестве возвращаемых значений. Паники можно избежать, проверяя типы с помощью пакета reflect, но я оставлю это как упражнение.

Я не знаю, каков полный сценарий, но может быть возможно заменить все это закрытием аргументов и результатов:

 var result *item
var err error
do := func() { result, err = todo("Param") }
do()
  

Комментарии:

1. Большое вам спасибо за ваш ответ. Я многому научился, читая ваш код.