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类

  这三个类上面已经说过了,直接用提供的接口转就可以,下面写几个需要注意的点:

  1. string虽然也是一个引用类型,但是clr在拿到这个string的指针时,还需要将这个string的数据直接复制进clr中才算转型结束(xlua也已经封装好了,不用我们自己去复制)。
  2. 大部分类型转型失败的时候都不会报错,而是会返回一个默认值。就拿将一个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;
}
  1. 当一个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主要分两种。

  1. 把c#对象存在ObjectTranslator中,用下标作为引用(类似于lua中的reference)。
  2. 经过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对象的。

  一共就两个接口:

  1. luaL_ref:把栈顶元素加入一个lua的表中,并返回下标。
  2. 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<int>使用
//其实LuaFunction.Cast就是干这个的,这里只是用简单的方式表达出来
public static Action<int> 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

  1. 通过ObjectTranslator.getDelegateUsingGeneric生成委托时,会对返回值和参数进行不为值类型的约束。因为值类型在il2cpp下会有jit异常。这也是为什么我们发现有的委托类型不用注册也可以使用,但是有的就不行。
  2. 在编辑器模式下,没有进行代码生成时,会通过Emit直接生成一个XLuaGenDelegateImplx类,内容和通过代码生成后的DelegateBridge一样,而不是全部通过反射来进行转型。让没有进行代码生成时的环境和真机环境更接近。
  3. DelegateBridge一般不会被直接引用,而是被bindto中的委托生成的闭包引用和被delegate_bridges作为弱引用持有。当一个DelegateBridge的bindto中的委托没有被任何对象引用时,这个DelegateBridge就会在下次gc时被gc掉。

其他

  这里主要写了常用lua类型转型的简介和一些关键点。可能不够全面和细节。

  如果有什么错误或者问题可以在下面留言。

xlua中lua对象到c#对象的转型的更多相关文章

  1. xLua中Lua调用C#

    xLua中Lua调用C# 1.前提 这里使用的是XLua框架,需要提前配置xlua,设置加载器路径: 可以参考之前的Blog:<xlua入门基础>: //调用段,所有的lua代码都写在Lu ...

  2. 【第二篇】xLua中lua加载方式

     xLua中lua文件加载方式 1. 直接执行字符串方式 LuaEnv luaenv = new LuaEnv(); luaenv.DoString("CS.UnityEngine.Debu ...

  3. xLua中C#调用Lua

    C#调用Lua 一.前提 这里使用的是XLua框架,需要提前配置xlua,设置加载器路径: 可以参考之前的Blog:<xlua入门基础>: 二.C#调用Lua全局变量 lua中所有的全局变 ...

  4. DDD 领域驱动设计-“臆想”中的实体和值对象

    其他博文: DDD 领域驱动设计-三个问题思考实体和值对象 DDD 领域驱动设计-三个问题思考实体和值对象(续) 以下内容属于博主"臆想",如有不当,请别当真. 扯淡开始: 诺兰的 ...

  5. ZeroMQ接口函数之 :zmq_msg_init_data - 从一个指定的存储空间中初始化一个ZMQ消息对象的数据

    ZeroMQ 官方地址 :http://api.zeromq.org/4-1:zmq_msg_init_data zmq_msg_init_data(3) ØMQ Manual - ØMQ/3.2.5 ...

  6. 前端学习 第二弹: JavaScript中的一些函数与对象(1)

    前端学习 第二弹: JavaScript中的一些函数与对象(1) 1.apply与call函数 每个函数都包含两个非继承而来的方法:apply()和call(). 他们的用途相同,都是在特定的作用域中 ...

  7. ORACLE DBA_OBJECTS视图中OBJECT_TYPE为LOB的对象查看

    在ORACLE数据库中,DBA_OBJECTS视图中OBJECT_TYPE为LOB的对象是什么东西呢?其实OBJECT_TYPE为LOB就是大对象(LOB),它指那些用来存储大量数据的数据库字段.下面 ...

  8. 我心目中的Asp.net核心对象

    转:http://www.cnblogs.com/fish-li/archive/2011/08/21/2148640.html 阅读目录 开始 HttpRuntime HttpServerUtili ...

  9. ES6新特性:Javascript中的Map和WeakMap对象

    Map对象 Map对象是一种有对应 键/值 对的对象, JS的Object也是 键/值 对的对象 : ES6中Map相对于Object对象有几个区别: 1:Object对象有原型, 也就是说他有默认的 ...

随机推荐

  1. Tensorflow从0到1(一)之如何安装Tensorflow(Windows和Linux两种版本)

    现在越来越多的人工智能和机器学习以及深度学习,强化学习出现了,然后自己也对这个产生了点兴趣,特别的进行了一点点学习,就通过这篇文章来简单介绍一下,关于如何搭建Tensorflow以及如何进行使用.建议 ...

  2. flask之Flask-session三方组件

    from flask import Flask, views, render_template, request, session, redirect import redis as redis #p ...

  3. removebg抠图小工具

    由于比较简单,直接上代码(removebg接口官网),更多小工具获取 (1)官网API,需注册获取X-Api-Key:removebg_官网api.py import requests respons ...

  4. C# winform DataGridView 绑定数据的的几种方法

    1.用DataSet和DataTable为DataGridView提供数据源 String strConn = "Data Source=.;Initial Catalog=His;User ...

  5. 安装sql_server_2008问题

    http://support.microsoft.com/kb/968382 --安装sql_server_2008_R2提示重启计算机规则失败 http://blog.sina.com.cn/s/b ...

  6. 从无到有Springboot整合Spring-data-jpa实现简单应用

    本文介绍Springboot整合Spring-data-jpa实现简单应用 Spring-data-jpa是什么?这不由得我们思考一番,其实通俗来说Spring-data-jpa默认使用hiberna ...

  7. 【Linux】Xshell 配置密钥登陆

    设置不需要密码登陆 vim /etc/ssh/sshd_config 在配置文件中参数的意义 PubkeyAuthentication yes #启用公告密钥配对认证方式 AuthorizedKeys ...

  8. SQL查找大小为n的连续区间

    数据准备 create table sequence ( seq int not null primary key ); insert into values(3); insert into valu ...

  9. Java的字节流,字符流和缓冲流对比探究

    目录 一.前言 二.字节操作和字符操作 三.两种方式的效率测试 3.1 测试代码 3.2 测试结果 3.3 结果分析 四.字节顺序endian 五.综合对比 六.总结 一.前言 所谓IO,也就是Inp ...

  10. Sniffer截包工具的使用

    Sniffer软件的安装 sniffer需要在xp或者win2003环境下才能正常运行,如果没有这两个系统,可以安装虚拟机,在虚拟机上使用sniffer.如果没有这两个系统就会出现找不到网卡或者打不开 ...