Toby Hede
Toby Hede

Reputation: 37143

Discover the return type of the current function in go

I have a function that is being generated using reflection and reflect.MakeFunc, so I don't actually have the return type until runtime.

Inside the template function that MakeFunc is using, is there a way to determine the return type of the concrete function being templated?

Essentially, is there a way to determine the return type iof the currently executing function at runtime?

I know about the Out method:

fn.Type().Out(0)

And I can find the return type of a function easily enough?

But is there a way to find the return type of the currently executing function (as opposed to an explicit passed function reference).

Upvotes: 2

Views: 4781

Answers (3)

andybalholm
andybalholm

Reputation: 16160

In the case you are talking about, the return type of the currently executing function is always []reflect.Type (because that is what a function passed to reflect.MakeFunc must return). What you really want is the return type of the reflect.makeFuncStub function that called your function.

There is no way to get that (except perhaps some strange inspection of the call stack), but you can make an enhanced version of MakeFunc that provides the information:

package main

import (
    "fmt"
    "reflect"
)

// MakeFunc is like reflect.MakeFunc, but fn has an extra argument, retType, which
// is passed the desired return type.
func MakeFunc(typ reflect.Type, fn func(args []reflect.Value, retType reflect.Type) (results []reflect.Value)) reflect.Value {
    if n := typ.NumOut(); n != 1 {
        panic("wrong number of return values")
    }
    rt := typ.Out(0)
    return reflect.MakeFunc(typ, func(args []reflect.Value) (results []reflect.Value) {
        return fn(args, rt)
    })
}

func makeReturnOne(fptr interface{}) {
    fn := reflect.ValueOf(fptr).Elem()
    fn.Set(MakeFunc(fn.Type(), returnOne))
}

func returnOne(args []reflect.Value, retType reflect.Type) []reflect.Value {
    ret := reflect.New(retType).Elem()
    switch retType.Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        ret.SetInt(1)
    case reflect.Float32, reflect.Float64:
        ret.SetFloat(1.0)
    default:
        panic("returnOne only supports int and float types")
    }
    r := ret.Interface()
    fmt.Printf("returning %v as %T\n", r, r)
    return []reflect.Value{ret}
}

func main() {
    var r1f func() float64
    var r1i func() int
    makeReturnOne(&r1f)
    makeReturnOne(&r1i)
    fmt.Println(r1f())
    fmt.Println(r1i())
}

Upvotes: 3

Ainar-G
Ainar-G

Reputation: 36279

I might have misinterpreted what you are trying to achieve, but why not just take the kind of the value you are returning? Modifying OneOfOne's example as follows:

fnTmpl := func(in []reflect.Value) (res []reflect.Value) {
    res = []reflect.Value{in[0]}
    fmt.Println("Returned:", res[0].Kind())
    return res
}

Playground: http://play.golang.org/p/EujmxyGRrI

Upvotes: 1

OneOfOne
OneOfOne

Reputation: 99391

You should check fn.Type().Out(0).Kind(), for example:

func main() {    
    fnTmpl := func(in []reflect.Value) []reflect.Value {
        return []reflect.Value{in[0]}
    }
    makeFn := func(fptr interface{}) {
        fn := reflect.ValueOf(fptr).Elem()
        fn.Set(reflect.MakeFunc(fn.Type(), fnTmpl))
    }
    var nFn func(int) int
    makeFn(&nFn)
    kind := reflect.TypeOf(nFn).Out(0).Kind()
    switch kind {
    case reflect.Int:
        fmt.Println("int")

    }
}

Upvotes: 3

Related Questions