xlua中lua对象到c#对象的转型
xlua中lua对象到c#对象的转型
lua中的类型
基础类型
define LUA_TNIL 0
define LUA_TBOOLEAN 1
define LUA_TLIGHTUSERDATA 2
define LUA_TNUMBER 3
define LUA_TSTRING 4
define LUA_TTABLE 5
define LUA_TFUNCTION 6
define LUA_TUSERDATA 7
define LUA_TTHREAD 8
变体(或者说子类型)
/*
** tags for Tagged Values have the following use of bits:
* bits 0-3: actual tag (a LUA_T value)
** bits 4-5: variant bits
** bit 6: whether value is collectable
*/
/*
** LUA_TFUNCTION variants:
** 0 - Lua function
** 1 - light C function
** 2 - regular C function (closure)
*/
/ Variant tags for functions /
define LUA_TLCL (LUA_TFUNCTION | (0 << 4)) / Lua closure /
define LUA_TLCF (LUA_TFUNCTION | (1 << 4)) / light C function /
define LUA_TCCL (LUA_TFUNCTION | (2 << 4)) / C closure /
/ Variant tags for strings /
define LUA_TSHRSTR (LUA_TSTRING | (0 << 4)) / short strings /
define LUA_TLNGSTR (LUA_TSTRING | (1 << 4)) / long strings /
/ Variant tags for numbers /
define LUA_TNUMFLT (LUA_TNUMBER | (0 << 4)) / float numbers /
define LUA_TNUMINT (LUA_TNUMBER | (1 << 4)) / integer numbers /
/ Bit mark for collectable types /
define BIT_ISCOLLECTABLE (1 << 6)
lua中的对象都是用TValue来描述的,TValue中的tt_成员变量代表着这个TValue的类型。关于类型的具体定义,上面贴的代码中的注释中已经讲的比较清楚了。
一个lua对象的类型是由一个7位的bits描述的。比如一个整数,这个对象的类型就是0011000(24)表示这个对象是数字类型中的整形,是一个不可回收对象。
C#如何获取lua对象
和c语言和lua交互其实没啥本质区别,就是通过lua提供的c函数操作lua栈,直接从栈中取就可以了。区别在于如何把取到的值转换为c#认识的值。
如何在C#端描述这些类型
简介
lua的类型中boolean、string、number这几个类型是clr所认识的类型,所以clr就可以直接把这些类型拿过来用。具体就是直接调用Lua提供的lua_tonumber之类的c接口。
lightUserData、table、function、userData、thread是C#不认识的类,需要通过某种标识(lua自带的reference系统)来表示。
boolean、string、number类
这三个类上面已经说过了,直接用提供的接口转就可以,下面写几个需要注意的点:
string虽然也是一个引用类型,但是clr在拿到这个string的指针时,还需要将这个string的数据直接复制进clr中才算转型结束(xlua也已经封装好了,不用我们自己去复制)。
大部分类型转型失败的时候都不会报错,而是会返回一个默认值。就拿将一个lua对象转为int来说,最终是通过lua_tointegerx函数调用的,当lua对象不是number类型时,返回0:
LUA_API lua_Integer lua_tointegerx (lua_State L, int idx, int pisnum) {
lua_Integer res;
const TValue *o = index2addr(L, idx);
int isnum = tointeger(o, &res);
if (!isnum)
res = 0; /* call to 'tointeger' may change 'n' even if it fails */
if (pisnum) *pisnum = isnum;
return res;
}
当一个number类型是浮点数时,转型整数不会进行取整操作,而是会直接返回0。因为lua默认对float转int的操作模式LUA_FLOORN2I是0,代表碰见float转int时返回0。
/*
** try to convert a value to an integer, rounding according to 'mode':
** mode == 0: accepts only integral values
** mode == 1: takes the floor of the number
** mode == 2: takes the ceil of the number
*/
int luaV_tointeger (const TValue obj, lua_Integer p, int mode) {
TValue v;
again:
if (ttisfloat(obj)) {
lua_Number n = fltvalue(obj); lua_Number f = l_floor(n); if (n != f) { /* not an integral value? */ if (mode == 0) return 0; /* fails if mode demands integral value */ else if (mode > 1) /* needs ceil? */ f += 1; /* convert floor to ceil (remember: n != f) */ } return lua_numbertointeger(f, p);
}
else if (ttisinteger(obj)) {
*p = ivalue(obj); return 1;
}
else if (cvt2num(obj) &&
luaO_str2num(svalue(obj), &v) == vslen(obj) + 1) { obj = &v; goto again; /* convert result from 'luaO_str2num' to an integer */
}
return 0; / conversion failed /
}
userData
userData主要是lua对c#对象的引用,这里只简单说一下。
代表c#对象的userData主要分两种。
把c#对象存在ObjectTranslator中,用下标作为引用(类似于lua中的reference)。
经过GC优化的结构体和枚举,不存在ObjectTranslator中,而是把所有内容都打包到userdata中一起传入lua中。比如一个Vector3,那么xlua会把这个Vector3的x、y、z作为3个连续的float一起打包到userdata中。这样就避免了c#层的装箱、拆箱和gc操作。
对table与function的引用简介
这两个类型都是通过lua的reference系统来让c#持有对lua对象的引用。
lua reference系统
c#就是通过这个系统来持有不认识的lua对象的。
一共就两个接口:
luaL_ref:把栈顶元素加入一个lua的表中,并返回下标。
luaL_unref:把一个下标所代表元素从表中删除。
这样就可以用一个整数来让lua外的环境持有这个lua对象。
具体可以看下官方说明lua References
luaBase类
所有lua对象在c#中的基类,在初始化时通过luaL_ref生成lua对象的引用,在析构时通过luaL_unref移除引用。
对table的引用
LuaTable
一般情况下table在C#中被包装为LuaTable类,没啥特别的,只是在LuaBase的基础上增加了几个常用的函数。比如Get、Set之类的,让开发者可以避开一些不直观的栈操作。
Array、List、Dictionary
这几个都差不多。都是把table中的key和value全部拿出来,组成Array或Dictionaray。
接口、其他类
这两种转型是尝试把这个table看作对应的接口或类。
比如将一个table转为IEnumberator就是把table转为SystemCollectionsIEnumeratorBridge类(继承了LuaBase、实现了IEnumerator的类,由Xlua生成),这个类实现了MoveNext和Reset。实现方法就是调用一下table中对应名称的函数。
对function的引用
lua函数在c#中有两种表示:
LuaFunction
LuaFunction和luaTable差不多,也是在LuaBase的基础上增加了几个常用函数,Call、Action之类的。
DelegateBridge
为什么已经有LuaFunction还要一个DelegateBridge类?
因为我们在c#中拿到一个lua函数时,大多数时候是要作为一个委托来时用的。DelegateBridge就是用来化简这个转型操作的。
DelegateBridge的功能就是在持有lua函数引用的同时,将这个函数包装成各种各样的委托,让整个转型过程对开发人员无感知。
下面是一个不使用DelegateBridge,自己转型的例子,比较繁琐:
//将一个LuaFunction作为一个Action使用
//其实LuaFunction.Cast就是干这个的,这里只是用简单的方式表达出来
public static Action LuaFunctionToActionInt(XLua.LuaFunction luaFunction)
{
//由于luaFunction已经提供了Call操作封装了函数调用的各种栈操作,所以我们这里只需要用一个Action<int>把这个操作包装起来即可 return (x) => { luaFunction.Call(x); };
}
public static void Test()
{
XLua.LuaEnv luaEnv = new XLua.LuaEnv(); object[] rets = luaEnv.DoString("return function(x) CS.UnityEngine.Debug.LogError(\"print x: \"..x) end"); var luaFunction = (XLua.LuaFunction)rets[0]; Action<int> actionInt = LuaFunctionToActionInt(luaFunction); actionInt(10);
}
DelegateBridge重要成员
xlua在将lua函数转型的时候做了什么
Tips
通过ObjectTranslator.getDelegateUsingGeneric生成委托时,会对返回值和参数进行不为值类型的约束。因为值类型在il2cpp下会有jit异常。这也是为什么我们发现有的委托类型不用注册也可以使用,但是有的就不行。
在编辑器模式下,没有进行代码生成时,会通过Emit直接生成一个XLuaGenDelegateImplx类,内容和通过代码生成后的DelegateBridge一样,而不是全部通过反射来进行转型。让没有进行代码生成时的环境和真机环境更接近。
DelegateBridge一般不会被直接引用,而是被bindto中的委托生成的闭包引用和被delegate_bridges作为弱引用持有。当一个DelegateBridge的bindto中的委托没有被任何对象引用时,这个DelegateBridge就会在下次gc时被gc掉。
其他
这里主要写了常用lua类型转型的简介和一些关键点。可能不够全面和细节。
如果有什么错误或者问题可以在下面留言。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
PyPi 是什么
pypi 是 Python Package Index 的首字母简写,其实表示的是 Python 的 Packag 索引,这个也是 Python 的官方索引。 你需要的包(Package)基本上都可以从这里面找到。作为开源软件,你也希望能够贡献你的 Package 到这里供其他用户使用。 我们举个栗子,如果你希望你的 Python 程序能够下载金融数据,目前比较好用的金融数据来源是 Yahoo 和 Google。 你可能需要读取这 2 个平台的 API,然后做一个下载部分的代码,然后将这个代码整合到自己的项目中。 是不是好麻烦,这样你可以到 PyPi 到上面去找找有没有已经写过这个内容了,幸运的是,你真找到了,你找到了一个 package 叫做 yfinance。但是这个代码在远程,没有在本地呀,怎么用了? 你就需要讲需要的内容从 PyPi 上下载下来。 这个时候你只需要一个命令: pip install yfinance 就可以了,是不是非常方便。当然如果你有你自己的 Package 也可以发布上去。 如果你使用的是 Java 项目的话,你就将 PyPi 理解成 Maven 就行了。...
- 下一篇
OpenYurt 开箱测评 | 一键让原生 K8s 集群具备边缘计算能力
作者| 郑超 阿里云高级开发工程师 随着物联网技术以及 5G 技术的高速发展,将云计算的能力延伸至边缘设备端,并通过中心进行统一交付、管控,已成为云计算的重要发展趋势。为服务更多开发者把握这一趋势,5 月 29 日,阿里巴巴正式对外开源了基于 ACK@Edge(边缘集群托管服务)的云原生边缘计算框架—— OpenYurt。 自 OpenYurt 开源以来受到了开发者的关注,今天这篇文章将带大家快速上手 OpenYurt ,介绍如何使用 OpenYurt 提供的命令行管理工具 Yurtctl, 高效快速地部署 OpenYurt 集群。 OpenYurt 介绍 OpenYurt 主打“云边一体化”概念,依托 Kubernetes 强大的容器应用编排能力,满足了云-边一体化的应用分发、交付、和管控的诉求。相较于其他基于 Kubernetes
相关文章
文章评论
共有0条评论来说两句吧...