这是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{}
我希望现在有意义
答案 0 :(得分:1)
我错了 - 问题是使用[]
而不是奇异值:
val := reflect.ValueOf(args)
// Do some kind of conversion...
a := []reflect.Value{val}
result := f.Call(a)
答案 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类型的转换。可以添加其他类型的转换。