前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Go unsafe进阶】执行空接口中的函数:除了断言与反射,你还有更好的选择

【Go unsafe进阶】执行空接口中的函数:除了断言与反射,你还有更好的选择

作者头像
henrylee2cn
发布2019-04-04 15:09:07
4510
发布2019-04-04 15:09:07
举报
文章被收录于专栏:Go实战Go实战

假如我们有这样一个包:iface.go

代码语言:javascript
复制
package iface

func GetAddFunc() interface{} {
	return add
}

type i32 int32

func add(a, b i32) i32 {
	return a + b
}

希望可以在包外执行add函数,怎么办?此处,因为该函数签名是不可导出的,所以,正常思路是使用反射,代码可能是这样:

代码语言:javascript
复制
import (
	"fmt"
	"iface"
	"reflect"
)

func main() {
	addIface := iface.GetAddFunc()
	ret := reflect.ValueOf(addIface).Call([]reflect.Value{
	reflect.ValueOf(1),
	reflect.ValueOf(2),
})[0].Int()
	fmt.Println(ret) // expect: 3
}

但是,运行时发现 panic 了?!错误信息如下:

代码语言:javascript
复制
panic: reflect: Call using int as type iface.i32

这可咋办?使用断言?类型不可导出,更加不可能! 这时,unsafe的高阶用法就派上用场了。先上最终代码,再来分析实现思路:

代码语言:javascript
复制
import (
	"fmt"
	"iface"
	"unsafe"

	"github.com/henrylee2cn/goutil/tpack"
)

func main() {
	addIface := iface.GetAddFunc()
	ptr := tpack.Unpack(addIface).Pointer()
	add := *(*func(a, b int32) int32)(unsafe.Pointer(&ptr))
	fmt.Println(add(1, 2)) // Output: 3
}

该代码,使用了本人开源的unsafe进阶包tpack。 它可以帮助我们拿到函数的Pointer,之后我们就能通过unsafe强转成外部定义的等价类型func(a, b int32) int32,进而执行它。

这种方法不但适用范围广泛,而且执行时没有任何性能损耗(等价于直接调用iface.add

那么,让我们了解一下tpack.Unpack(iface.GetAddFunc()).Pointer()这行代码做了什么事情呢?

首先,addIface是接口类型,而接口类型的底层类型原型如下:

代码语言:javascript
复制
emptyInterface struct {
	typ  *rtype
	word unsafe.Pointer
}

typ字段存储类型信息,word字段存储指向数据的位置信息。我们当前需求只关心word。通过偏移量,我们可以拿到word的值,进而拿到函数在内存中的起始位置,即Pointer。

  • 计算word偏移量,在64位系统中,该值为8:
代码语言:javascript
复制
ptrOffset := unsafe.Offsetof(new(emptyInterface).word)
  • 获取word值:
代码语言:javascript
复制
word := uintptr(unsafe.Pointer(&addIface)) + ptrOffset
  • 拿到函数在内存中的起始位置:
代码语言:javascript
复制
ptr := *(*uintptr)(unsafe.Pointer(word))
  • 最后,进行强转类型,由于 ptr 表示函数的起始位置,而unsafe.Pointer要求是变量的指针,因此,需要使用 &ptr 进行指针类型的强转:
代码语言:javascript
复制
add := *(*func(a, b int32) int32)(unsafe.Pointer(&ptr))

更多有趣的unsafe进阶操作,可以了解 tpack

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2pkhijryroysk

(adsbygoogle = window.adsbygoogle || []).push({});

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档