将uintptr从反射转换为指针(*)

问题描述 投票:0回答:1

我只是找不到获取给定结构的每个属性的指针片段的方法。我正在使用反射来获取我的指针(感谢https://stackoverflow.com/a/24348352/6093604

if valueField.CanAddr() {
    address = fmt.Sprintf("0x%X", valueField.Addr().Pointer())
}

正如你所看到的,valueField.Addr()。Pointer()返回一个指针addr值,但是,使用反射,我想获得一个可用的sql.Rows.Scan()指针

所以我做的是:

func StructAttributesToPointersSlice(object interface{}) []interface{} {

    val := reflect.ValueOf(object)

    if val.Kind() == reflect.Interface && !val.IsNil() {
        elm := val.Elem()
        if elm.Kind() == reflect.Ptr && !elm.IsNil() && elm.Elem().Kind() == reflect.Ptr {
            val = elm
        }
    }
    if val.Kind() == reflect.Ptr {
        val = val.Elem()
    }

    var ptrs []interface{}
    for i := 0; i < val.NumField(); i++ {
        valueField := val.Field(i)

        if valueField.Kind() == reflect.Ptr {
            valueField = valueField.Elem()

        }
        if valueField.CanAddr() {
            ptrs = append(ptrs, valueField.Addr().Pointer())
        }
    }

    return ptrs
}

但是当我尝试将它用于Scan() sql函数时:

var values []interface{}

// Iterate over each rows got from the query
for rows.Next() {

    ptrs := utils.StructAttributesToPointersSlice(&newObject)

    for _, item := range ptrs {
        fmt.Println(reflect.TypeOf(item))
    }

    err = rows.Scan(ptrs...)
    if err != nil {
        return nil, model.Status{Code: http.StatusInternalServerError, Error: err.Error()}
    } else {
        values = append(values, newObject)
    }
}

我收到此错误:

sql:列索引0上的扫描错误:目标不是指针

我知道这是因为它不是好的类型,因为它是uintptr,但那么如何将它转换为可用的指针?

谢谢

pointers go
1个回答
0
投票

使用unsafe.Pointer将uintptr转换为某种类型的指针。例如,以下表达式将uintptr u转换为T指针:

(*T)(unsafe.Pointer(u))

这种转换在StructAttributesToPointersSlice中没有帮助,因为struct字段可以是任何类型。此外,不需要从uintptr转换,也不安全。

表达式valueField.Addr()是指向字段的指针的reflect.Value。调用Interface()获取实际指针。要修复程序,请更改

ptrs = append(ptrs, valueField.Addr().Pointer())

ptrs = append(ptrs, valueField.Addr().Interface())

这是该函数的简化版本:

func StructAttributesToPointersSlice(object interface{}) []interface{} {
    v := reflect.ValueOf(object)
    if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
        panic("argument must be a pointer to struct")
    }

    v = v.Elem()
    var result []interface{}
    for i := 0; i < v.NumField(); i++ {
        f := v.Field(i)
        if !f.CanSet() {
            continue
        }
        result = append(result, f.Addr().Interface())

    }
    return result
}

关于此代码的一些注意事项

  • 参数必须是指向结构的指针。
  • 没有必要在字段上调用CanAddr。检查指向struct的指针涵盖了这一点。
  • CanSet()跳过未导出的字段。您可能希望它发生恐慌。
  • 该函数对调用者中的错误感到恐慌。请考虑返回错误。

playground example

© www.soinside.com 2019 - 2024. All rights reserved.