UE4 Unlua源码解析7 - Lua通过UE命名空间访问C++类型的实现原理


举例 UE4.UKismetSystemLibrary.PrintString(“hello”)

我们来看Unlua提供的例子的HelloWorld

代码是

local hello = “HelloWorld”
UE4.UKismetSystemLibrary.PrintString(hello)

这个例子我们最终会调到UE4的方法,并且成功执行,那么首先,我们要解答的问题就是为什么Lua这么写最终能调用到C++的方法,

而且Lua传的参数是Lua的字符串,为什么C++能成功执行,这简单的代码,背后到底做了什么事情

1.1 UE4发生了 什么

Unlua.lua中声明了UE4,可以看到UE4其实就是全局表_G,而且他的元表是global_mt,Index元方法为global_index,我们去看看global_index

在这里插入图片描述

第73行拿到传入的索引k,检查第一个字母是不是UE的类的首字母,是就调用RegisterClass,如果是E,就调用RegisterEnum,然后调用rawget,rawget方法是不使用Index元方法的取值函数,可以猜测上面的函数执行完之后,t表也就是UE4表里已经就有了索引k的值了。

具体的执行逻辑接着看

在这里插入图片描述

1.2 UE4.UKismetSystemLibrary发生了什么

由上节讲到的,UE4.UKismetSystemLibrary的时候,因为UE4是表,所以就去表里找UKismetSystemLibrary,但是第一次肯定是找不到的,于是走到UE4的元表的元方法Index,也就是执行global_index,传进来的t是UE4,k是“UKismetSystemLibrary”

由之前的分析可以看到代码会走到RegisterClass,然后传入k

RegisterClass在lua测的声明如下

在这里插入图片描述

看到LuaContext.cpp里

在这里插入图片描述

lua api lua_register前面讲到了,将C函数f设置为全局名称name的新值,lua端可以通过name调用C方法f

所以这行代码将C函数Global_RegisterClass注册,在Lua端通过Lua全局名称RegisterClass调用

Global_RegisterClass和RegisterClass逐行解释之前讲过了,此处只说结果

Global_RegisterClass 读虚拟栈的大小,当小于1的时候返回,否则调用RegisterClass,参数传(虚拟机,栈底的值转成C 的string)

此时虚拟栈的内容是传进来类名“UKismetSystemLibrary”,以及参数的数量1,所以传递给RegisterClass的参数是(虚拟机L,“UKismetSystemLibrary”)

RegisterClass逐行解释之前讲过了,总之是两大部分

1 根据UE4反射生成UClass的描述信息FClassDesc ,然后存储到UnLua的存储全局反射数据的GReflectionRegistry对象中。

2 根据生成的FClassDesc 为其在Lua中注册table,然后设置table的元表为自己,然后往table里塞一些元方法

RegisterClass结束后,UE4.UKismetSystemLibrary就是一个Lua端的表了,这个表的元表是自己,并且元方法Index是LuaCore中的方法Class_Index

类图如下

在这里插入图片描述

1.3 UE4.UKismetSystemLibrary.PrintString发生了什么

截至目前,UE4.UKismetSystemLibrary就能拿到上一步生成的Lua table了,此时调用.PrintString的时候,可以看到表里没用这个数据,所以走到元表的Index元方法,之前我们知道,元方法为LuaCore中的Class_Index,所以重点看这个方法即可

其中最重要的是走到GetField

GetField和RegiestClass很像,就像是注册类一样,GetField主要也做两件事

1

​ 如果传进来的是属性,就根据UE4反射生成FPropertyDesc

​ 如果传进来的是方法,就根据UE4反射生成FFunctionDesc,并且把FPropertyDesc或FFunctionDesc存在FClassDesc中

2

​ 如果是属性 就在表里存FPropertyDesc,然后将FPropertyDesc压栈,作为返回值返回

​ 如果是方法,就在表里存FFunctionDesc+C方法Class_CallUFunction的闭包进去,FFunctionDesc作为Upvalue。然后将FFunctionDesc+C方法Class_CallUFunction的闭包压栈,作为返回值返回

此时的类图如下

在这里插入图片描述

1.4 UE4.UKismetSystemLibrary.PrintString(“Hello”)发生了什么

截至目前,相当于调用LuaCore的Class_CallUFunction

传进来的参数是lua的string hello

根据之前的解释C++通过闭包里的FFunctionDesc执行方法CallUE,参数是Lua传进去的,通过

1)执行PreCall,根据【UFunctionDesc】和Lua参数,将Lua参数转换成C++参数,再根据【UFunctionDesc】反射信息,将C++参数写入到一个缓存区中

2)执行 UObject::ProcessEvent(FinalFunction, Params),参数FinalFunction是UFunction,参数Params是前面保存有C++参数的缓存区。ProcessEvent执行时,即调用了真正我们想调用的C++ Create函数,然后把C++返回值放入了Params缓存区中

3)执行PostCall,从Params缓存区中读出C++返回值,转换成Lua返回值,Push进Lua栈,返回给Lua。

结束,至此,屏幕应该输出Hello

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

珞珈大胖强TURBO

谢谢兄弟们,我会一直努力出货的

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值