获取任何Go结构的大小的通用函数

huangapple go评论93阅读模式
英文:

generic function to get size of any structure in Go

问题

我正在编写一个通用函数来获取任何类型结构的大小,类似于C语言中的sizeof函数。

我尝试使用接口和反射来实现,但是无法得到正确的结果。以下是代码:

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	type myType struct {
		a int
		b int64
		c float32
		d float64
		e float64
	}
	info := myType{1, 2, 3.0, 4.0, 5.0}
	getSize(info)
}

func getSize(T interface{}) {
	v := reflect.ValueOf(T)
	const size = unsafe.Sizeof(v)
	fmt.Println(size)
}

这段代码返回错误的结果12。我对Go语言非常陌生,请帮助我解决这个问题。

英文:

I am writing a generic function to get the size of any type of structure, similar to sizeof function in C.

I am trying to do this using interfaces and reflection but I'm not able to get the correct result. Code is below:

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	type myType struct {
		a int
		b int64
		c float32
		d float64
		e float64
	}
	info := myType{1, 2, 3.0, 4.0, 5.0}
	getSize(info)
}

func getSize(T interface{}) {
	v := reflect.ValueOf(T)
	const size = unsafe.Sizeof(v)
	fmt.Println(size)
}

This code returns wrong result as 12. I am very new to Go, kindly help me on this.

答案1

得分: 13

你正在获取 reflect.Value 结构体的大小,而不是接口 T 中包含的对象的大小。幸运的是,reflect.Type 有一个 Size() 方法:

size := reflect.TypeOf(T).Size()

这将给出40,这是合理的,因为有填充。

英文:

You're getting the size of the reflect.Value struct, not of the object contained in the interface T. Fortunately, reflect.Type has a Size() method:

size := reflect.TypeOf(T).Size()

This gives me 40, which makes sense because of padding.

答案2

得分: 2

Go 1.18

在Go 1.18中,你可以使用泛型函数和unsafe.Sizeof

func getSize[T any]() uintptr {
    var v T
    return unsafe.Sizeof(v)
}

请注意,这种方法比使用reflect更高效,但会在你的代码中引入unsafe,一些静态分析工具可能会对此发出警告。

然而,如果你的目标是改善代码重用性或在运行时获取大小(请继续阅读以获取解决方案),这并不会有太大帮助,因为你仍然需要使用正确的实例化调用该函数:

type myType struct {
    a int
    b int64
    c float32
    d float64
    e float64
}

func main() {
    fmt.Println(getSize[myType]())
}

当作为其他泛型代码的一部分使用时,可能会发挥最大的作用,例如将类型参数传递给getSize的泛型类型或函数。不过,如果你有参数v,这等效于直接调用unsafe.Sizeof(v)。使用函数仍然有用,可以隐藏对unsafe的使用。一个简单的例子:

func printSize[T any](v T) {
    // (对v进行操作)
    // 实例化并调用
    s := getSize[T]() 
    // s := unsafe.Sizeof(v)
    
    fmt.Println(s)
}

否则,你可以向getSize传递一个实际参数。然后类型推断将使得不需要指定类型参数。这段代码可能更灵活,允许你在运行时传递任意参数,同时保持避免反射的好处:

func getSize[T any](v T) uintptr {
    return unsafe.Sizeof(v)
}

func main() {
    type myType struct {
        a int
        b int64
        c float32
        d float64
        e float64
    }
    info := myType{1, 2, 3.0, 4.0, 5.0}
    
    // 推断类型参数
    fmt.Println(getSize(info))       // 40
    fmt.Println(getSize(5.0))        // 8
    fmt.Println(getSize([]string{})) // 24
    fmt.Println(getSize(struct {
        id uint64
        s  *string
    }{}))                            // 16
}

Playground: https://go.dev/play/p/kfhqYHUwB2S

英文:

Go 1.18

With Go 1.18 you can use a generic function with unsafe.Sizeof:

func getSize[T any]() uintptr {
    var v T
    return unsafe.Sizeof(v)
}

Note that this will be more performant than using reflect, but it will introduce unsafe in your code base — some static analysis tools may give warnings about that.

However if your goal is to improve code reuse or get sizes at run time (read on for the solution to that), this won't help much because you still need to call the function with proper instantiation:

type myType struct {
    a int
    b int64
    c float32
    d float64
    e float64
}

func main() {
    fmt.Println(getSize[myType]())
}

You might get the most out of this when used as part of some other generic code, e.g. a generic type or function where you pass a type param into getSize. Although if you have the argument v this is equivalent to calling unsafe.Sizeof(v) directly. Using a function could be still useful to hide usage of unsafe. A trivial example:

func printSize[T any](v T) {
    // (doing something with v)
    // instantiate with T and call
	s := getSize[T]() 
    // s := unsafe.Sizeof(v)
    
	fmt.Println(s)
}

Otherwise you can pass an actual argument to getSize. Then type inference will make it unnecessary to specify the type param. This code perhaps is more flexible and allows you to pass arbitrary arguments at runtime, while keeping the benefits of avoiding reflection:

func getSize[T any](v T) uintptr {
    return unsafe.Sizeof(v)
}

func main() {
    type myType struct {
        a int
        b int64
        c float32
        d float64
        e float64
    }
    info := myType{1, 2, 3.0, 4.0, 5.0}
    
    // inferred type params
    fmt.Println(getSize(info))       // 40
	fmt.Println(getSize(5.0))        // 8
	fmt.Println(getSize([]string{})) // 24
	fmt.Println(getSize(struct {
		id uint64
		s  *string
	}{}))                            // 16
}

Playground: https://go.dev/play/p/kfhqYHUwB2S

huangapple
  • 本文由 发表于 2015年7月10日 18:32:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/31338588.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定