Golang中的泛型与类型转换探讨
Golang中的泛型与类型转换探讨 我想编写一个简单的方法,将Go基本类型(int(8/16/32) / uint(8/16/32))转换为cgo类型(C.int / C.uint 等)。
我想使用泛型,但在类型转换时遇到了问题。
请看以下代码:
// wrapCType是一个泛型方法,用于将GOTYPE(int32/float32等)转换为CTYPE(c_int/c_float等)
func wrapCType[CTYPE any, GOTYPE any](goValue *GOTYPE) (wrapped *CTYPE, finisher func()) {
if goValue != nil {
cValue := CTYPE(any(*goValue))
wrapped = &cValue
finisher = func() {
*goValue = GOTYPE(any(cValue))
}
} else {
finisher = func() {}
}
return
}
更多关于Golang中的泛型与类型转换探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
这行不通,因为 CTYPE 和 GOTYPE 可以是任何类型,所以你不能直接使用类型转换语法。你或许可以通过为 CTYPE 和 GOTYPE 添加约束来实现,像这样:
type CNumber interface {
~C.char | ~C.short | ~C.int | ~C.long |
~C.uchar | ~C.ushort | ~C.uint | ~C.ulong |
C.float | C.double
}
type GoNumber interface {
~int8 | ~int16 | ~int32 | ~int64 |
~uint8 | ~uint16 | ~uint32 | ~uint64 |
~float32 | ~float64
}
func wrapCType[CTYPE CNumber, GOTYPE GoNumber](goValue *GOTYPE) (wrapped *CTYPE, finisher func())
但我不太确定;我最近没在电脑前,而且当你 import "C" 时,Go Playground 无法工作。
更多关于Golang中的泛型与类型转换探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
哦,好的,看来我已经修复了这个问题 😄
ype CNumber interface {
C.char | C.short | C.int | C.long |
C.uchar | C.ushort | C.uint | C.ulong |
C.float | C.double
}
type GoNumber interface {
~int8 | ~int16 | ~int32 | ~int64 |
~uint8 | ~uint16 | ~uint32 | ~uint64 |
~float32 | ~float64
}
// wrapCType is a generic method to convert GOTYPE (int32/float32 e.t.c.) into CTYPE (c_int/c_float e.t.c.)
func wrapCType[CTYPE CNumber, GOTYPE GoNumber](goValue *GOTYPE) (wrapped *CTYPE, finisher func()) {
if goValue != nil {
cValue := CTYPE(*goValue)
wrapped = &cValue
finisher = func() {
*goValue = GOTYPE(cValue)
}
} else {
finisher = func() {}
}
return
}
不过,Goland 对这个转换有警告。
type CNumber interface {
~C.char | ~C.short | ~C.int | ~C.long |
~C.uchar | ~C.ushort | ~C.uint | ~C.ulong |
C.float | C.double
}
type GoNumber interface {
~int8 | ~int16 | ~int32 | ~int64 |
~uint8 | ~uint16 | ~uint32 | ~uint64 |
~float32 | ~float64
}
感谢你的回答!
遗憾的是,它产生了以下错误:
../type_wrapper.go:33:2: invalid use of ~ (underlying type of _Ctype_char is int8)
../type_wrapper.go:33:12: invalid use of ~ (underlying type of _Ctype_short is int16)
../type_wrapper.go:33:23: invalid use of ~ (underlying type of _Ctype_int is int32)
../type_wrapper.go:33:32: invalid use of ~ (underlying type of _Ctype_long is int64)
../type_wrapper.go:34:3: invalid use of ~ (underlying type of _Ctype_uchar is uint8)
../type_wrapper.go:34:14: invalid use of ~ (underlying type of _Ctype_ushort is uint16)
../type_wrapper.go:34:26: invalid use of ~ (underlying type of _Ctype_uint is uint32)
../type_wrapper.go:34:36: invalid use of ~ (underlying type of _Ctype_ulong is uint64)
另外,可以问一下 ~ 是什么吗?
gucio321:
另外,可以问一下
~是什么吗?
是的:
类型约束中的波浪号 ("~")
在 Go 中,泛型类型可以有约束(这就是为什么我们把 wrapCType[CTYPE any, ...] 换成了 wrapCType[CTYPE CNumber, ...]),这些约束被定义为接口。当你写下类似这样的代码时:
type Integer interface {
int | int8 | int16 | int32 | int64
}
func add[T Integer](a, b T) T { return a + b }
Integer 约束表示类型 T 必须是 int、int8 等。
然而,如果你这样做:
type MyInt int
var a MyInt = 1
var b MyInt = 2
c := add(a, b)
这将无法工作,因为 a 和 b 的类型是 MyInt,而不是 int,而 MyInt 不在约束列表中。
在像 ~int 这样的约束中,波浪号 (~) 意味着类型不需要精确地是 int;它可以是任何底层类型为 int 的类型(例如我例子中的 MyInt)。
gucio321:
遗憾的是,它产生了以下错误:
../type_wrapper.go:33:2: invalid use of ~ (underlying type of _Ctype_char is int8)
一些背景信息...
当你写 import "C" 时得到的 “C” 包,并不是像 “fmt” 包那样的 “真实” 包。它是一个 “虚拟” 包,告诉 Go 编译器在 Go 和 C 的约定之间进行 “转换”(例如,编译器在调用 Go 函数和调用 C 函数时会做不同的事情,等等)。我对 cgo 不是特别熟悉,所以我不确定编译器对 C.int、C.char 等类型使用了什么样的 “魔法”。根据那个错误信息,看起来 C.char 被转换为一个内部的 _Ctype_char 类型,并且该类型的底层类型是 int8(所以就像在 Cgo 的某个地方写着 type char = _Ctype_char 和 type _Ctype_char = int8)。
–
总之,基于这个错误:
underlying type of _Ctype_char is int8
以及其他类似的错误,看起来你并不需要 CNumber 接口,实际上一切都归结为 GoNumber,所以你可以直接说:
func wrapCType[CTYPE, GOTYPE GoNumber](goValue *GOTYPE) (wrapped *CTYPE, finisher func())
不过,在这一点上,我不确定该建议什么,因为我不确定所有这些类型之间的所有转换是否都被允许。你能展示一下你将如何使用这个 wrapCType 函数的例子吗?也许我或其他人在看到你想要做什么之后会有更好的建议。
在Go泛型中直接进行类型转换确实存在限制,因为泛型类型参数在编译时会被擦除为接口类型。以下是针对CGo类型转换的几种解决方案:
方案1:使用类型断言(推荐)
func wrapCType[CTYPE any, GOTYPE any](goValue *GOTYPE) (wrapped *CTYPE, finisher func()) {
if goValue != nil {
// 使用类型断言进行转换
var cValue CTYPE
switch v := any(*goValue).(type) {
case int32:
cValue = any(C.int(v)).(CTYPE)
case uint32:
cValue = any(C.uint(v)).(CTYPE)
case float32:
cValue = any(C.float(v)).(CTYPE)
// 添加其他类型转换
default:
panic("unsupported type")
}
wrapped = &cValue
finisher = func() {
switch v := any(cValue).(type) {
case C.int:
*goValue = any(int32(v)).(GOTYPE)
case C.uint:
*goValue = any(uint32(v)).(GOTYPE)
case C.float:
*goValue = any(float32(v)).(GOTYPE)
default:
panic("unsupported type")
}
}
} else {
finisher = func() {}
}
return
}
方案2:使用接口约束和类型转换函数
type CGoConvertible interface {
~int32 | ~uint32 | ~float32 | ~int64 | ~uint64 | ~float64
}
func wrapCType[CTYPE any, GOTYPE CGoConvertible](goValue *GOTYPE) (wrapped *CTYPE, finisher func()) {
if goValue != nil {
// 通过中间变量进行转换
var cValue CTYPE
switch any(*goValue).(type) {
case int32:
cValue = CTYPE(C.int(*goValue))
case uint32:
cValue = CTYPE(C.uint(*goValue))
case float32:
cValue = CTYPE(C.float(*goValue))
}
wrapped = &cValue
finisher = func() {
switch any(cValue).(type) {
case C.int:
*goValue = GOTYPE(int32(cValue.(C.int)))
case C.uint:
*goValue = GOTYPE(uint32(cValue.(C.uint)))
case C.float:
*goValue = GOTYPE(float32(cValue.(C.float)))
}
}
} else {
finisher = func() {}
}
return
}
方案3:使用反射(灵活但性能较低)
import "reflect"
func wrapCType[CTYPE any, GOTYPE any](goValue *GOTYPE) (wrapped *CTYPE, finisher func()) {
if goValue != nil {
v := reflect.ValueOf(*goValue)
var cValue CTYPE
switch v.Kind() {
case reflect.Int32:
cValue = any(C.int(v.Int())).(CTYPE)
case reflect.Uint32:
cValue = any(C.uint(v.Uint())).(CTYPE)
case reflect.Float32:
cValue = any(C.float(v.Float())).(CTYPE)
}
wrapped = &cValue
finisher = func() {
cv := reflect.ValueOf(cValue)
switch cv.Type() {
case reflect.TypeOf(C.int(0)):
reflect.ValueOf(goValue).Elem().Set(reflect.ValueOf(int32(cv.Int())))
case reflect.TypeOf(C.uint(0)):
reflect.ValueOf(goValue).Elem().Set(reflect.ValueOf(uint32(cv.Uint())))
case reflect.TypeOf(C.float(0)):
reflect.ValueOf(goValue).Elem().Set(reflect.ValueOf(float32(cv.Float())))
}
}
} else {
finisher = func() {}
}
return
}
方案4:针对特定类型对的专用函数
func WrapInt32(goValue *int32) (wrapped *C.int, finisher func()) {
if goValue != nil {
cValue := C.int(*goValue)
wrapped = &cValue
finisher = func() {
*goValue = int32(cValue)
}
} else {
finisher = func() {}
}
return
}
func WrapUint32(goValue *uint32) (wrapped *C.uint, finisher func()) {
if goValue != nil {
cValue := C.uint(*goValue)
wrapped = &cValue
finisher = func() {
*goValue = uint32(cValue)
}
} else {
finisher = func() {}
}
return
}
这些方案解决了Go泛型在类型转换时的限制问题。方案1和2在类型安全和性能之间取得了平衡,方案3提供了最大的灵活性但牺牲了性能,方案4则是最直接的类型安全解决方案。

