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