Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrapper for arbitrary function in Go

Tags:

go

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.

like image 860
Alexander Ponomarev Avatar asked Apr 19 '14 05:04

Alexander Ponomarev


2 Answers

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))
}
like image 178
joshlf Avatar answered Oct 09 '22 16:10

joshlf


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)
}
like image 39
Alexander Ponomarev Avatar answered Oct 09 '22 16:10

Alexander Ponomarev