Home  >  Article  >  Backend Development  >  Is there a good way to make numeric types suitable for "method.Call" in golang?

Is there a good way to make numeric types suitable for "method.Call" in golang?

WBOY
WBOYforward
2024-02-08 23:06:081197browse

Is there a good way to make numeric types suitable for method.Call in golang?

php editor Youzi In golang, when using the method.Call method to call a function, you may encounter some problems with numeric type parameters. However, there are some ways we can solve this problem. First, we can convert the numeric type to the corresponding reflect.Value type before passing it to the method.Call method. In addition, we can also use the reflection method to obtain the parameter type of the function and perform corresponding processing according to the parameter type. In short, through these methods, we can make the numeric type suitable for the invocation of the method.Call method in golang, thereby solving this problem.

Question content

Code Go Playground:

package main

import (
    "fmt"
    "reflect"
)

func (s StructWithManyMethods) Func1(a int, b uint, c float64) {
    fmt.Printf("func:Func1 a:%d b:%d c:%f \n", a, b, c)
}
func (s StructWithManyMethods) Func2(a string, b int, c int, d int) {
    fmt.Printf("func:Func2 a:%s b:%d c:%d d:%d\n", a, b, c, d)
}

type StructWithManyMethods struct {
}

func (s StructWithManyMethods) CallMethod(n string, p []interface{}) {
    method := reflect.ValueOf(s).MethodByName(n)
    methodType := method.Type()
    for i := 0; i < methodType.NumIn(); i++ {
        in := methodType.In(i)
        switch in.Kind() {
        case reflect.Float32:
            switch v := p[i].(type) {
            case float64:
                p[i] = float32(v)
            case float32:
                p[i] = float32(v)
            case int:
                p[i] = float32(v)
            case uint:
                p[i] = float32(v)
            case int8:
                p[i] = float32(v)
            case uint8:
                p[i] = float32(v)
            case int16:
                p[i] = float32(v)
            case uint16:
                p[i] = float32(v)
            case int32:
                p[i] = float32(v)
            case uint32:
                p[i] = float32(v)
            case int64:
                p[i] = float32(v)
            case uint64:
                p[i] = float32(v)
            }
        case reflect.Float64:
            switch v := p[i].(type) {
            case float64:
                p[i] = float64(v)
            case float32:
                p[i] = float64(v)
            case int:
                p[i] = float64(v)
            case uint:
                p[i] = float64(v)
            case int8:
                p[i] = float64(v)
            case uint8:
                p[i] = float64(v)
            case int16:
                p[i] = float64(v)
            case uint16:
                p[i] = float64(v)
            case int32:
                p[i] = float64(v)
            case uint32:
                p[i] = float64(v)
            case int64:
                p[i] = float64(v)
            case uint64:
                p[i] = float64(v)
            }
        case reflect.Int:
            switch v := p[i].(type) {
            case float64:
                p[i] = int(v)
            case float32:
                p[i] = int(v)
            case int:
                p[i] = int(v)
            case uint:
                p[i] = int(v)
            case int8:
                p[i] = int(v)
            case uint8:
                p[i] = int(v)
            case int16:
                p[i] = int(v)
            case uint16:
                p[i] = int(v)
            case int32:
                p[i] = int(v)
            case uint32:
                p[i] = int(v)
            case int64:
                p[i] = int(v)
            case uint64:
                p[i] = int(v)
            }
        case reflect.Uint:
            switch v := p[i].(type) {
            case float64:
                p[i] = uint(v)
            case float32:
                p[i] = uint(v)
            case int:
                p[i] = uint(v)
            case uint:
                p[i] = uint(v)
            case int8:
                p[i] = uint(v)
            case uint8:
                p[i] = uint(v)
            case int16:
                p[i] = uint(v)
            case uint16:
                p[i] = uint(v)
            case int32:
                p[i] = uint(v)
            case uint32:
                p[i] = uint(v)
            case int64:
                p[i] = uint(v)
            case uint64:
                p[i] = uint(v)
            }
        case reflect.Int8:
            switch v := p[i].(type) {
            case float64:
                p[i] = int8(v)
            case float32:
                p[i] = int8(v)
            case int:
                p[i] = int8(v)
            case uint:
                p[i] = int8(v)
            case int8:
                p[i] = int8(v)
            case uint8:
                p[i] = int8(v)
            case int16:
                p[i] = int8(v)
            case uint16:
                p[i] = int8(v)
            case int32:
                p[i] = int8(v)
            case uint32:
                p[i] = int8(v)
            case int64:
                p[i] = int8(v)
            case uint64:
                p[i] = int8(v)
            }
        case reflect.Uint8:
            switch v := p[i].(type) {
            case float64:
                p[i] = uint8(v)
            case float32:
                p[i] = uint8(v)
            case int:
                p[i] = uint8(v)
            case uint:
                p[i] = uint8(v)
            case int8:
                p[i] = uint8(v)
            case uint8:
                p[i] = uint8(v)
            case int16:
                p[i] = uint8(v)
            case uint16:
                p[i] = uint8(v)
            case int32:
                p[i] = uint8(v)
            case uint32:
                p[i] = uint8(v)
            case int64:
                p[i] = uint8(v)
            case uint64:
                p[i] = uint8(v)
            }
        case reflect.Int16:
            switch v := p[i].(type) {
            case float64:
                p[i] = int16(v)
            case float32:
                p[i] = int16(v)
            case int:
                p[i] = int16(v)
            case uint:
                p[i] = int16(v)
            case int8:
                p[i] = int16(v)
            case uint8:
                p[i] = int16(v)
            case int16:
                p[i] = int16(v)
            case uint16:
                p[i] = int16(v)
            case int32:
                p[i] = int16(v)
            case uint32:
                p[i] = int16(v)
            case int64:
                p[i] = int16(v)
            case uint64:
                p[i] = int16(v)
            }
        case reflect.Uint16:
            switch v := p[i].(type) {
            case float64:
                p[i] = uint16(v)
            case float32:
                p[i] = uint16(v)
            case int:
                p[i] = uint16(v)
            case uint:
                p[i] = uint16(v)
            case int8:
                p[i] = uint16(v)
            case uint8:
                p[i] = uint16(v)
            case int16:
                p[i] = uint16(v)
            case uint16:
                p[i] = uint16(v)
            case int32:
                p[i] = uint16(v)
            case uint32:
                p[i] = uint16(v)
            case int64:
                p[i] = uint16(v)
            case uint64:
                p[i] = uint16(v)
            }
        case reflect.Int32:
            switch v := p[i].(type) {
            case float64:
                p[i] = int32(v)
            case float32:
                p[i] = int32(v)
            case int:
                p[i] = int32(v)
            case uint:
                p[i] = int32(v)
            case int8:
                p[i] = int32(v)
            case uint8:
                p[i] = int32(v)
            case int16:
                p[i] = int32(v)
            case uint16:
                p[i] = int32(v)
            case int32:
                p[i] = int32(v)
            case uint32:
                p[i] = int32(v)
            case int64:
                p[i] = int32(v)
            case uint64:
                p[i] = int32(v)
            }
        case reflect.Uint32:
            switch v := p[i].(type) {
            case float64:
                p[i] = uint32(v)
            case float32:
                p[i] = uint32(v)
            case int:
                p[i] = uint32(v)
            case uint:
                p[i] = uint32(v)
            case int8:
                p[i] = uint32(v)
            case uint8:
                p[i] = uint32(v)
            case int16:
                p[i] = uint32(v)
            case uint16:
                p[i] = uint32(v)
            case int32:
                p[i] = uint32(v)
            case uint32:
                p[i] = uint32(v)
            case int64:
                p[i] = uint32(v)
            case uint64:
                p[i] = uint32(v)
            }
        case reflect.Int64:
            switch v := p[i].(type) {
            case float64:
                p[i] = int64(v)
            case float32:
                p[i] = int64(v)
            case int:
                p[i] = int64(v)
            case uint:
                p[i] = int64(v)
            case int8:
                p[i] = int64(v)
            case uint8:
                p[i] = int64(v)
            case int16:
                p[i] = int64(v)
            case uint16:
                p[i] = int64(v)
            case int32:
                p[i] = int64(v)
            case uint32:
                p[i] = int64(v)
            case int64:
                p[i] = int64(v)
            case uint64:
                p[i] = int64(v)
            }
        case reflect.Uint64:
            switch v := p[i].(type) {
            case float64:
                p[i] = uint64(v)
            case float32:
                p[i] = uint64(v)
            case int:
                p[i] = uint64(v)
            case uint:
                p[i] = uint64(v)
            case int8:
                p[i] = uint64(v)
            case uint8:
                p[i] = uint64(v)
            case int16:
                p[i] = uint64(v)
            case uint16:
                p[i] = uint64(v)
            case int32:
                p[i] = uint64(v)
            case uint32:
                p[i] = uint64(v)
            case int64:
                p[i] = uint64(v)
            case uint64:
                p[i] = uint64(v)
            }
        }
    }
    parameterValues := make([]reflect.Value, 0)
    for _, e := range p {
        parameterValues = append(parameterValues, reflect.ValueOf(e))
    }
    method.Call(parameterValues)
}

func main() {
    var s StructWithManyMethods
    s.CallMethod("Func1", []interface{}{1.0, 2.0, 3})
    s.CallMethod("Func2", []interface{}{"test", 1, 2, 3.0})
}

Output:

func:Func1 a:1 b:2 c:3.000000 
func:Func2 a:test b:1 c:2 d:3

Because I need to call the method dynamically, but the parameters come from different data formats, which leads to the need to convert the numerical type. For example, when parsing JSON in Golang, an unspecified number type is treated as float64, even though it probably should be an int.

The code looks bad but runs fine.

I'm wondering if there is a better way to do this.

Solution

Use the reflect API to convert the value.

func (s StructWithManyMethods) CallMethod(n string, p []interface{}) error {
    method := reflect.ValueOf(s).MethodByName(n)
    methodType := method.Type()
    parameterValues := make([]reflect.Value, methodType.NumIn())
    if len(p) < len(parameterValues) {
        return fmt.Errorf("expected %d parameters, got %d", len(parameterValues), len(p))
    }
    for i := range parameterValues {
        in := methodType.In(i)
        v := reflect.ValueOf(p[i])
        if !v.CanConvert(in) {
            return fmt.Errorf("cannot convert p[%d] from %s to %s", i, v.Type(), in)
        }
        parameterValues[i] = reflect.ValueOf(p[i]).Convert(in)
    }
    method.Call(parameterValues)
    return nil
}

To prevent panics, this code checks whether the conversion is allowed before converting.

Run code on PlayGround!

The above is the detailed content of Is there a good way to make numeric types suitable for "method.Call" in golang?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete