1
votes

I need to create a function that wraps an inner function, and has exactly the same signature as the inner function. I am fearing that this is not possible since Go does not support generics, but perhaps it is achievable using reflect? The following is the pseudo-go I would like to have:

func myFunc(a int, b string) (string, error) {
    return string(a) + b, nil
}

func wrapInner(inner interface{}) interface{} {
    argTypes := argsOf(inner)
    returnTypes := returnsOf(inner)

    wrapper := func(args argTypes) returnTypes {
        // do something with inner's args
        modArgs := doSomething(args)
        ret := inner(modArgs)
        // do something with the return
        modRet := doSomething(ret)
    }
    return wrapper
}

wrapped := wrapInner(myFunc)
val, err := wrapped(1, "b")

The pseudo-code is full of errors, but the idea is that wrapInner has no clue about the signature of inner. However, it is able to inspect the signature (using, perhaps, reflect?) and creates a function that adds logic to inner and has exactly the same signature as inner. Is this possible?

1
No, not in the way you're calling it. You'd have to return it as an interface{} which means you'd have to call it using reflection. There's no way to return a function with an signature that isn't known at compile time and call it directly.Adrian
As someone else said, this isn't possible in Go. It sounds like you're trying to solve a problem as if you were in a language with generics, maye if you explain what you're trying to do in more detail, someone could explain to you a more Go-like solution to the problem? Thanks!Sam Whited

1 Answers

0
votes

What you are trying to achieve is the middleware pattern. It is commonly implemented with interfaces. You would have to manually implement the middleware function for every function you wish to annotate.

Here is an example:

package main

import (
    "fmt"
    "strconv"
)

type Service interface {
    myFunc(a int, b string) (string, error)
}

type implService struct{}

func (s implService) myFunc(a int, b string) (string, error) {
    return strconv.Itoa(a) + b, nil
}

type loggingMiddleware struct {
    next Service
}

func (s loggingMiddleware) myFunc(a int, b string) (string, error) {
    fmt.Println(a, b)
    return s.next.myFunc(a, b)
}

func main() {
    var myservice Service = &implService{}

    myservice = &loggingMiddleware{
        next: myservice,
    }
    result, err := myservice.myFunc(1, "a") // prints 1 a
    fmt.Println(result, err)                // prints 1a <nil>

}