使用反射解码参数

时间:2017-12-04 03:22:07

标签: go

这是Function and argument unmarshalling in go?的后续问题。

给出以下接口变量。

var args interface{}

假设它是一个例子,它包含这些字节:

[[74 111 104 110] [32 97 110 100 32 74 97 110 101]]

即。两个字符串“John”,“和Jane”

由MethodByName

获取的函数值
f := reflect.ValueOf(s.functions).MethodByName("Hello")
if f.IsValid() {
  val := reflect.ValueOf(args)
  //  Do some kind of conversion...

  result := f.Call(val) // This won't compile. Wrong type.  How do I massage args of type interface{} into what's expected by the call.

}

如果失败,我并不特别在意。我将使用recover来捕获调用失败。

以下是更多细节:

var req struct {
    Ver      int
    MsgID    int
    FuncName string
    Args     interface{}
}

dec := codec.NewDecoder(frame, s.h)

err = dec.Decode(&req)
if err != nil {
    s.log.Println(err)
    break
}

fmt.Println("New Msg:")
fmt.Printf(" ver  : %d\n", req.Ver)
fmt.Printf(" id   : %d\n", req.MsgID)
fmt.Printf(" func : %s\n", req.FuncName)

f := reflect.ValueOf(s.functions).MethodByName(req.FuncName)
if f.IsValid() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()

    fmt.Println("Function is: ", req.FuncName)
    var callArgs []reflect.Value
    args := reflect.ValueOf(req.Args)
    t := f.Type()
    for i := 0; i < t.NumIn(); i++ {
        t := t.In(i)
        v := reflect.New(t).Elem()
        if i < args.Len() {
            // Convert arg to type of v and set.
            arg := args.Index(i)
            switch t.Kind() {
            case reflect.String:
                v.SetString(string(reflect.Value.Bytes(arg)))
            case reflect.Slice:
                if t.Elem() == reflect.TypeOf(byte(0)) {
                    v.SetBytes(reflect.Value.Bytes(arg))
                } else {
                    panic("not supported")
                }
            case reflect.Int:
                //i, err := strconv.ParseInt(string(arg), 10, 0)
                // if err != nil {
                //  panic("bad int")
                // }
                // v.SetInt(i)
            default:
                panic("not supported")
            }
        }
        // Collect arguments for the call below.
        callArgs = append(callArgs, v)
    }
    result := f.Call(callArgs)
    fmt.Println(result)

    val := reflect.ValueOf(req.Args)
    a := []reflect.Value{val}

    r := f.Call(a)
    fmt.Println("Returned", r[0], r[1])
}

输出:

新消息:  ver:2  id:1  func:你好 功能是:你好 在f中恢复反映:在界面值

上调用reflect.Value.Bytes

注意:这是一个RPC API。我有一个函数名称(请参阅问题顶部的链接)和一些作为数组传递的参数。我的示例中的参数是字符串,但可以传递给函数。这是功能所需要的。这是通用的。

e.g。

Hello(name ... string)string

添加(n ... int)int

DoSomething(字符串,b int,c bool)

即。我正在解组这些论点,我不知道它们是什么。 除了我知道他们将被传递到切片和 抛入变量Args,其类型为interface{}我希望现在有意义

2 个答案:

答案 0 :(得分:1)

我错了 - 问题是使用[]而不是奇异值:

  val := reflect.ValueOf(args)

  //  Do some kind of conversion...
  a := []reflect.Value{val}

  result := f.Call(a) 

https://play.golang.org/p/AqQama-LJv

答案 1 :(得分:1)

尝试以下方法。

基本思想是通过循环遍历函数参数来为reflect.Call调用创建一个[] reflect.Value。对于每个参数,将传入的参数类型转换为函数调用中预期的类型。

var req = struct {
    Ver      int
    MsgID    int
    FuncName string
    Args     interface{}
}{
    Args: []interface{}{[]byte("John"), "Jane", 123, "456"},
}
args := req.Args.([]interface{})

var funcs Funcs
f := reflect.ValueOf(funcs).MethodByName("Hello")
var callArgs []reflect.Value
t := f.Type()
// For each function argument ...
for i := 0; i < t.NumIn(); i++ {
    t := t.In(i)
    v := reflect.New(t).Elem()
    if i < len(args) {
        // Convert arg to type of v and set.
        arg := args[i]
        switch t.Kind() {
        case reflect.String:
            switch arg := arg.(type) {
            case string:
                v.SetString(arg)
            case []byte:
                v.SetString(string(arg))
            default:
                panic("not supported")
            }
        case reflect.Slice:
            if t.Elem() != reflect.TypeOf(byte(0)) {
                panic("not supported")
            }
            switch arg := arg.(type) {
            case string:
                v.SetBytes([]byte(arg))
            case []byte:
                v.SetBytes(arg)
            default:
                panic("not supported")
            }

        case reflect.Int:
            switch arg := arg.(type) {
            case int:
                v.SetInt(int64(arg))
            case string:
                i, err := strconv.ParseInt(arg, 10, 0)
                if err != nil {
                    panic("bad int")
                }
                v.SetInt(i)
            default:
                panic("not supported")
            }
        default:
            panic("not supported")
        }
    }
    // Collect arguments for the call below.
    callArgs = append(callArgs, v)
}

result := f.Call(callArgs)
fmt.Println(result)

该代码段包含string,[] byte和int类型的转换。可以添加其他类型的转换。

playground example