使用反射的过程将结构字段绑定到命令行标志值
我有几个配置结构,我想自动将其解析到接受的命令行标志中(允许用户通过CLI覆盖它们)。鉴于结构可能会随着时间的流逝而发展,并且其中一个结构是接口{}
,反射似乎是正确的方法。我只需要解析字符串,INT和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做这项工作?”,
我稍微更广泛的问题
- 是不需要不安全的指针铸造的反射方法更好?”
我更喜欢完全尊重类型系统,但是如何通过反思做到这一点并不明显。另一种选择似乎是代码生成,我想避免使用,但是如果需要,可以纠缠。
I have a several configuration structures that I want to automatically parse into accepted command line flags (to allow a user to override them via CLI). Given that the structures are likely to evolve over time, and one of the structures is an interface{}
, reflection seems to be the correct approach. I only need to parse strings, ints, and float64s. I've gotten the following working:
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)
}
}
}
}
But when I uncomment the float64 block I get a panic:
panic: reflect.Value.UnsafeAddr of unaddressable value
goroutine 1 [running]:
reflect.Value.UnsafeAddr(...)
/usr/local/go/src/reflect/value.go:2526
So, my concrete question is
- "Is there a way to make this work for float64s?",
and my slightly broader question is
- "Is there a better approach with reflection that wouldn't require the unsafe pointer casting?"
I'd much prefer to fully respect the type system, but it's not obvious how to do this with reflection. The other alternative seems like it would be with code generation, which I'd like to avoid, but can wrangle if needed.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果我正确理解您的要求,则无需使用
Unsafe
。要获取指向字段的指针,您只需使用value.addr
方法和类型断言获得混凝土类型。https://go.dev/play/pplay/p/1f2kyo0cbuj
If I understood your requirements correctly then there's NO need to use
unsafe
. To get a pointer to a field you can just use theValue.Addr
method and type assertions to get the concrete type.https://go.dev/play/p/1F2Kyo0cBuj