使用反射程序将结构字段绑定到命令行标志值

我有几个配置结构,我想自动将其解析为可接受的命令行标志(以允许用户通过 CLI 覆盖它们)。鉴于结构可能会随着时间的推移而演变,并且其中一个结构是interface{},反射似乎是正确的方法。我只需要解析字符串、整数和 float64。我得到了以下工作:


func ReconGenerateFlags(in *ReconConfig, cmd *cobra.Command) {


    for _, f := range reflect.VisibleFields(reflect.TypeOf(*in)) {


        group_name := f.Name


        v := reflect.ValueOf(in).Elem().FieldByName(f.Name).Elem() // Return the concrete substructures pointed to by "in"

        sub_fields := reflect.VisibleFields(v.Type())


        for _, sub_f := range sub_fields {


            tag_name := sub_f.Name

            sub_v := v.FieldByName(tag_name)

            full_flag_name := strings.ToLower(group_name) + "." + strings.ToLower(tag_name)


            switch s := sub_v.Type().String(); s {

            case "string":

                ptr := (*string)(unsafe.Pointer(sub_v.UnsafeAddr()))

                cmd.Flags().StringVar(ptr, flag_name, "", "")

            case "int":

                ptr := (*int)(unsafe.Pointer(sub_v.UnsafeAddr()))

                cmd.Flags().IntVar(ptr, flag_name, 0, "")

            //case "float64":

            //  ptr := (*float64)(unsafe.Pointer(sub_v.UnsafeAddr()))

            //  cmd.Flags().Float64Var(ptr, flag_name, 0.0, "")

            default:

                fmt.Printf("unrecognized type in config setup: %s\n", s)

            }


        }


    }

}


但是当我取消注释 float64 块时,我感到恐慌:


panic: reflect.Value.UnsafeAddr of unaddressable value


goroutine 1 [running]:

reflect.Value.UnsafeAddr(...)

    /usr/local/go/src/reflect/value.go:2526

所以,我的具体问题是


“有没有办法使它适用于 float64s?”,

我稍微宽泛的问题是


“有没有更好的反射方法不需要不安全的指针转换?”

我更愿意完全尊重类型系统,但如何通过反射做到这一点并不明显。另一种选择似乎是代码生成,我想避免这种情况,但如果需要可以争论。


在此先感谢您的任何输入!


月关宝盒
浏览 93回答 1
1回答

波斯汪

如果我正确理解了您的要求,那么就没有必要使用unsafe.&nbsp;要获取指向字段的指针,您只需使用Value.Addr方法和类型断言来获取具体类型。func GenerateFlags(in interface{}, fs *flag.FlagSet, names []string) {&nbsp; &nbsp; rv := reflect.ValueOf(in)&nbsp; &nbsp; if rv.Kind() != reflect.Ptr || rv.Elem().Kind() != reflect.Struct {&nbsp; &nbsp; &nbsp; &nbsp; return // exit if not pointer-to-a-struct&nbsp; &nbsp; }&nbsp; &nbsp; rv = rv.Elem()&nbsp; &nbsp; rt := rv.Type()&nbsp; &nbsp; for i := 0; i < rt.NumField(); i++ {&nbsp; &nbsp; &nbsp; &nbsp; sf := rt.Field(i)&nbsp; &nbsp; &nbsp; &nbsp; fv := rv.Field(i)&nbsp; &nbsp; &nbsp; &nbsp; name := strings.Join(append(names, strings.ToLower(sf.Name)), ".")&nbsp; &nbsp; &nbsp; &nbsp; switch fv.Type() {&nbsp; &nbsp; &nbsp; &nbsp; case reflect.TypeOf(string("")):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; p := fv.Addr().Interface().(*string)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fs.StringVar(p, name, "", "")&nbsp; &nbsp; &nbsp; &nbsp; case reflect.TypeOf(int(0)):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; p := fv.Addr().Interface().(*int)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fs.IntVar(p, name, 0, "")&nbsp; &nbsp; &nbsp; &nbsp; case reflect.TypeOf(float64(0)):&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; p := fv.Addr().Interface().(*float64)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fs.Float64Var(p, name, 0, "")&nbsp; &nbsp; &nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; names := append([]string{}, names...)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; GenerateFlags(fv.Interface(), fs, append(names, strings.ToLower(sf.Name)))&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}https://go.dev/play/p/1F2Kyo0cBuj
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go