Function Wrapper in Go
Asked Answered
T

2

6

I need a function wrapper, which will take a function and return the wrapper version of it. What I try to achieve is inject some code before and after of the function's execution

func funcWrapper(myFunc interface{}){
    fmt.Println("Before")
    //call myFunc
    fmt.Println("After")
}
Tamboura answered 16/2, 2018 at 12:58 Comment(5)
Wrote up an answer and realized it wasn't actually what you asked. Have you tried looking at how they did it here: #23166911?Odum
something like this may help if the signature is known: play.golang.org/p/Dbv39l8cOaaDundee
Signatures are known but it is varying, there are about 10 types of signatures I need to handleTamboura
Try something like closure to wrap your function.Plesiosaur
This can be solved quite elegantly now IMHO: https://mcmap.net/q/1075963/-wrapper-for-arbitrary-function-in-goNorfolk
I
9

If you know the signature of the function, you can create a function that takes a function value of that function type, and returns another function value of the same type. You may use a function literal that does the extra functionality you want to add to it, and call the passed function when it's appropriate.

For example let's say we have this function:

func myfunc(i int) int {
    fmt.Println("myfunc called with", i)
    return i * 2
}

A function that takes an int and returns an int (double of its input number).

Here's a possible wrapper that "annotates" it with logging before and after calling it, also logging its input and return value:

func wrap(f func(i int) int) func(i int) int {
    return func(i int) (ret int) {
        fmt.Println("Before, i =", i)
        ret = f(i)
        fmt.Println("After, ret =", ret)
        return
    }
}

Example testing it:

wf := wrap(myfunc)
ret := wf(2)
fmt.Println("Returned:", ret)

Output (try it on the Go Playground):

Before, i = 2
myfunc called with 2
After, ret = 4
Returned: 4

Since Go does not support generics, you have to do this for each different function types you want to support. Or you may attempt to write a general solution using reflect.MakeFunc() as you can see in this question: Wrapper for arbitrary function in Go, it it's gonna be a pain to use it.

If you want to support multiple function types, best would be to create a separate wrapper for each distinct function type, so each can have the proper return type (function type with proper parameter and result types). It could look like this if you'd also want to support wrapping functions with no parameter and return types:

func wrap(f func()) func() {
    return func() {
        fmt.Println("Before func()")
        f2()
        fmt.Println("After func()")
    }
}

func wrapInt2Int(f func(i int) int) func(i int) int {
    return func(i int) (ret int) {
        fmt.Println("Before func(i int) (ret int), i =", i)
        ret = f(i)
        fmt.Println("After func(i int) (ret int), ret =", ret)
        return
    }
}

You may do it in a single wrap() function like below, but its cons (less type safety, harder to use) out-weight its pros, so I'd advise against it and I would just create separate wrapper functions for separate function types.

Let's also support wrapping a function with no parameter and return types:

func myfunc2() {
    fmt.Println("myfunc2 called")
}

The wrapper function:

func wrap(f interface{}) interface{} {
    switch f2 := f.(type) {
    case func(i int) (ret int):
        return func(i int) (ret int) {
            fmt.Println("Before func(i int) (ret int), i =", i)
            ret = f2(i)
            fmt.Println("After func(i int) (ret int), ret =", ret)
            return
        }
    case func():
        return func() {
            fmt.Println("Before func()")
            f2()
            fmt.Println("After func()")
        }
    }
    return nil
}

Testing it:

wf := wrap(myfunc).(func(int) int)
ret := wf(2)
fmt.Println("Returned:", ret)

wf2 := wrap(myfunc2).(func())
wf2()

Output (try this one on the Go Playground):

Before func(i int) (ret int), i = 2
myfunc called with 2
After func(i int) (ret int), ret = 4
Returned: 4
Before func()
myfunc2 called
After func()

Since there is no generics in Go, this solution can only have a return type interface{}, and when using it, its return value have to be manually "converted", type asserted to the function type you expect it to return (e.g. wf2 := wrap(myfunc2).(func())).

See related: Go type for function call

Inceptive answered 16/2, 2018 at 13:28 Comment(0)
J
0

Here’s one way to do it https://play.golang.org/p/ouzU2jiFDz2


package main

import (
    "fmt"
)

func trace(funcName string) func() {
    fmt.Println("pre", funcName)
    return func() {
        fmt.Println("post", funcName)
    }
}

func doSomething(name string) {
    defer trace("something")()
    fmt.Println(name)
}

func main() {
    doSomething("test")
}
Jaquesdalcroze answered 3/2, 2021 at 14:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.