Is it possible to create a wrapper for arbitrary function in Go that would take the same arguments and return the same value?
I'm not talking about the wrapper that would look exactly the same, it may look differently, but it should solve the problem.
For example the problem might be to create a wrapper of arbitrary function that first looks for the result of the function call in cache and only in case of cache miss executes the wrapped function.
Here's a solution using reflect.MakeFunc. This particular solution assumes that your transformation function knows what to do with every different type of function. Watch this in action: http://play.golang.org/p/7ZM4Hlcqjr
package main
import (
"fmt"
"reflect"
)
type genericFunction func(args []reflect.Value) (results []reflect.Value)
// A transformation takes a function f,
// and returns a genericFunction which should do whatever
// (ie, cache, call f directly, etc)
type transformation func(f interface{}) genericFunction
// Given a transformation, makeTransformation returns
// a function which you can apply directly to your target
// function, and it will return the transformed function
// (although in interface form, so you'll have to make
// a type assertion).
func makeTransformation(t transformation) func(interface{}) interface{} {
return func(f interface{}) interface{} {
// g is the genericFunction that transformation
// produced. It will work fine, except that it
// takes reflect.Value arguments and returns
// reflect.Value return values, which is cumbersome.
// Thus, we do some reflection magic to turn it
// into a fully-fledged function with the proper
// type signature.
g := t(f)
// typ is the type of f, and so it will also
// be the type that of the function that we
// create from the transformation (that is,
// it's essentially also the type of g, except
// that g technically takes reflect.Value
// arguments, so we need to do the magic described
// in the comment above).
typ := reflect.TypeOf(f)
// v now represents the actual function we want,
// except that it's stored in a reflect.Value,
// so we need to get it out as an interface value.
v := reflect.MakeFunc(typ, g)
return v.Interface()
}
}
func main() {
mult := func(i int) int { return i * 2 }
timesTwo := func(f interface{}) genericFunction {
return func(args []reflect.Value) (results []reflect.Value) {
// We know we'll be getting an int as the only argument,
// so this type assertion will always succeed.
arg := args[0].Interface().(int)
ff := f.(func(int) int)
result := ff(arg * 2)
return []reflect.Value{reflect.ValueOf(result)}
}
}
trans := makeTransformation(timesTwo)
// Since mult multiplies its argument by 2,
// and timesTwo transforms functions to multiply
// their arguments by 2, f will multiply its
// arguments by 4.
f := trans(mult).(func(int) int)
fmt.Println(f(1))
}
The answer based on @joshlf13 idea and answer, but seems more simple to me. http://play.golang.org/p/v3zdMGfKy9
package main
import (
"fmt"
"reflect"
)
type (
// Type of function being wrapped
sumFuncT func(int, int) (int)
// Type of the wrapper function
wrappedSumFuncT func(sumFuncT, int, int) (int)
)
// Wrapper of any function
// First element of array is the function being wrapped
// Other elements are arguments to the function
func genericWrapper(in []reflect.Value) []reflect.Value {
// this is the place to do something useful in the wrapper
return in[0].Call(in[1:])
}
// Creates wrapper function and sets it to the passed pointer to function
func createWrapperFunction(function interface {}) {
fn := reflect.ValueOf(function).Elem()
v := reflect.MakeFunc(reflect.TypeOf(function).Elem(), genericWrapper)
fn.Set(v)
}
func main() {
var wrappedSumFunc wrappedSumFuncT
createWrapperFunction(&wrappedSumFunc)
// The function being wrapped itself
sumFunc := func (a int, b int) int {
return a + b
}
result := wrappedSumFunc(sumFunc, 1, 3)
fmt.Printf("Result is %v", result)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With