本节是对lua中的userdata的一个小小总结,其示例参考自《Lua程序设计》(第四版)

问题:在lua中使用布尔数组。

虽然lua中可以使用表(table)来实现布尔数组,但是其空间利用率极低。

因此,可以使用lua中的自定义类型实现。

1.位操作

首先是和位相关的操作,如下所示:

//对于x,获取第y位的值
#define getbit(x,y) (((x)>>(y)&1)
//对于x,把第y位置1。先把1左移y位,然后进行位或操作
#define setbit(x,y) x |= (1<<y)
//对于x,把第y位置0。先把1左移y位,然后取反,与x做位与操作
#define clrbit(x,y) x&=~(1<<y)

以上三种是在本示例中会用到的宏函数(宏替换)

2.内存对齐

这里会提到内存对齐的原因是和布尔数组的创建长度有关。内存对齐的文章有很多,这里不再赘述。

3.元表(metatable)和元方法(metamethod)

元表可以修改一个值在面对一个未知操作时的行为。例如,假设a和b都是表,那么可以通过元表定义lua语言如何计算表达式a+b,当lua语言视图将两个表相加时,它会先检查两者之一是否有元表企鹅该元表中是否有__add字段。如果找到了该字段,就调用该字段的值。(摘自《lua程序设计》第四版)。元方法类似于c++中的运算符重载,使用元表和元方法可以重新定义一些常见的行为。比如

  • __index 当访问一个表中不存在的字段时,解释器会首先查找一个名为__index的元方法,如果没有这个元方法,则会返回nil,否则,则由这个元方法来提供最终的结果。
  • __newindex 类似于__index,不同则在于__newindex用于表的更新而__index用于表的查询。当对一个表中不存在的索引赋值时,解释器会查找__newindex元方法,如果这个存在,那么解释器就调用它而不执行赋值。
  • __len lua中求表长度的语法是在表的前面加上#,比如表名为set,那么set的长度为#set,当解释器发现#set时,会查找__len的元方法,如果有,则调用该元方法并返回值。

4.userdata

最后则是本节的重头戏。

首先,需要构建一个结构体,用于保存布尔数组:

typedef struct BitArray
{int size;unsigned int values[1]; //可变部分
} BitArray;

在BitArray中,values为可变部分,详细内容可以参考这个帖子。简单的说,这种空数组的形式,可以理解为指针,但是要比指针更加的方便和安全。

对于32位的机器来说,根据默认的内存对齐,那么BitArray的总字节数为8个字节,也就是说,values一开始就有4个字节的空间(这个需要注意)。

然后是声明一些宏:

#define CHAR_BIT 8
//无符号整型的位数
#define BITS_PER_WORD (CHAR_BIT * sizeof(unsigned int))
//根据指定的索引计算存放相应比特位的字
#define I_WORD(i) ((unsigned int)(i) / BITS_PER_WORD)
//计算访问这个字中相应比特位中要用的掩码
#define I_BIT(i) (1 << ((unsigned int)(i) % BITS_PER_WORD))#define checkarray(pL) (BitArray*)luaL_checkudata(pL, 1, "LuaBook.array")

BITS_PER_WORD表示unsigned int所占的位数;I_WORD表示对于索引i来说,它在哪一个无符号整型上,而I_BIT则对应了一个掩码,通过它可以方便地取出或更改索引i所对应的值。而checkarray则相对来说比较复杂,它的主要功能就是用于标识本节所实现的布尔数组,必须要有一个名为LuaBook.array的元表。

static int newarray(lua_State* pL)
{int i;size_t nbytes;BitArray* array;//比特位的个数int n = (int)luaL_checkinteger(pL, 1);luaL_argcheck(pL, n >= 1, 1, "invalid size");//和内存对齐有关 BitArray 12字节 vlaues 之后默认有4个字节nbytes = sizeof(BitArray) + I_WORD(n - 1) * sizeof(unsigned int);array = lua_newuserdata(pL, nbytes);//设置元表luaL_getmetatable(pL, "LuaBook.array");lua_setmetatable(pL, -2);//初始化array->size = n;for (i = 0; i < I_WORD(n - 1); i++)array->values[i] = 0;return 1;
}

首先是创建布尔数组,该方法需要一个值,该值用于标识布尔数组的长度,newarray函数会根据这个长度调用lua_newusedata()来分配内存。需要注意的是,分配的布尔数组的内存可能比正常用到的要小一个字节,不过由于内存对齐的缘故,而多了4B,所以综合来看,上面分配内存会多3B或4B。

lua_newuserdata会返回一个开辟好的内存空间,并把该空间同时压入栈顶。后面的程序为这个userdata分配了一个名为LuaBook.array的元表。最后则是对布尔数组进行初始化为全0。

static unsigned int *getparams(lua_State* pL, unsigned int *mask)
{BitArray* array = checkarray(pL);int index = (int)luaL_checkinteger(pL, 2) - 1;luaL_argcheck(pL, 0 <= index && index < array->size, 2, "index out of range");*mask = I_BIT(index);return &array->values[I_WORD(index)];
}

getparams函数,用于从lua_State中获取到合法的BitArray,并返回索引index所对应的unsigned int(大小为4B的内存块)和掩码mask。

static int setarray(lua_State* pL)
{unsigned int mask;unsigned int *entry = getparams(pL, &mask);luaL_checkany(pL, 3);if (lua_toboolean(pL, 3))*entry |= mask;else*entry &= ~mask;return 0;
}

该函数会根据第三个参数的值,来觉得对索引为index,是置一操作还是清零操作。

static int getarray(lua_State* pL)
{unsigned int mask;unsigned int *entry = getparams(pL, &mask);lua_pushboolean(pL, *entry & mask);return 1;
}

该函数用于获取index对应的位的值。

static int getsize(lua_State* pL)
{BitArray* array = checkarray(pL);luaL_argcheck(pL, array != NULL, 1, "'array' expected");lua_pushinteger(pL, array->size);return 1;
}

getsize函数用于获取布尔数组的长度,并返回。

static const struct luaL_Reg arraylib_m[] =
{{"set", setarray},{"get", getarray},{"size", getsize},{"__tostring", array2string},{"__newindex", setarray},{"__index", getarray},{"__len", getsize},{NULL, NULL}
};static const struct luaL_Reg arraylib_f[] =
{{"new", newarray},{NULL, NULL}
};

这两个变量的作用下面会提到。

int lua_openarray(lua_State* pL)
{//创建元表luaL_newmetatable(pL, "LuaBook.array");//复制元表/*lua_pushvalue(pL, -1);//mt.__index = mtlua_setfield(pL, -2, "__index");*///注册元方法luaL_setfuncs(pL, arraylib_m, 0);//创建一个新的表,并把这些函数注册到这个表中luaL_newlib(pL, arraylib_f);return 1;
}

lua_openarray函数是加载布尔数组的重要一步,在该函数中,首先创建一个名称为LuaBook.array的元表,并复制了移分,然后让 mt.__index = mt,接着把arraylib_m中的方法全部都注册到了这个元表之中,这一步我目前有些疑问(经我个人测试,上面的那两句注释后不影响)。

lua_newlib(pL, arraylib_f)创建了一个新的表,并把这些函数注册到这个表中。

在lua_openarray方法中,虽然创建了LuaBook.array元表,但是绑定元表的过程在newarray中。

#include <cstdio>
#include <string>#include "lua.hpp"extern "C"
{#include "bool_array.h"
}int main()
{char buffer[256];int error = 0;//打开lualua_State* pL = luaL_newstate();//打开所有的标准库luaL_openlibs(pL);//注册函数luaL_requiref(pL, "array", lua_openarray, 1);lua_pop(pL, 1);while (fgets(buffer, sizeof(buffer), stdin) != NULL){//编译用户输入的每一行内容,并向栈中压入编译后得到的函数//以保护模式弹出编译后的函数运行error = luaL_loadstring(pL, buffer) || lua_pcall(pL, 0, 0, 0);if (error){fprintf(stderr, "%s\n", lua_tostring(pL, -1));lua_pop(pL, 1);}}lua_close(pL);return 0;
}

然后则是把布尔数组加载进来,它主要的代码是这句:

 //注册函数luaL_requiref(pL, "array", lua_openarray, 1);

到这一步,我们已经注册了一个array.new方法在lua环境中了。

运行上述程序,我们就可以愉快的玩耍了,示例如下:

a = array.new(100)
a[3] = true
print(a[3])
print(a)

lua中的自定义类型:userdata相关推荐

  1. C语言中的自定义类型

    C语言中的自定义类型 (一)结构体 结构体的声明 结构体是一些值的集合,这些值称为成员变量,结构体的成员可以是不同类型的变量: 结构体的声明 struct tag { member-list; }va ...

  2. java converter转换器_在SpringMVC中设置自定义类型转换器Converter

    前言 在SpringMVC中为我们提供了许多内置的类型转换器,当我们在HTML表单中发起一个请求时,Spring会根据表单项中name属性的值映射到POJO的属性名,调用相对性属性的set方法帮我们把 ...

  3. springboot中mongodb自定义类型转换器

    文章目录 1 场景 1.1 BigDecimal写入mongo 1.2 人工转换 1.3 自定义转换器 2 版本 3 步骤 3.1 定义转换器 3.2 配置mongoDb工厂类 3.3 加载自定义转换 ...

  4. 在NSUserDefaults中存储自定义类型的数据

    将自定义的类的数据以数组的形式直接存储到NSUserDefaults中会报错,需要进行转换,且需要将该类实现NSCoding协议. e.g. 存储过程 NSMutableArray *archiveA ...

  5. vector中针对自定义类型的排序

    本文是从我一个实际的程序摘出来,因此没有太多的叙述性的东西 首先呢 sort需要一个头文件 1  #include<algorithm> 这种排序主要针对的是自定义的vector类型 如: ...

  6. python ctypes 回调函数_Python ctypes中具有自定义类型的回调

    那里有一些错误,有些是基本的Python错误: from ctypes import * class A(Structure): _fields_ = [ ("a1", c_cha ...

  7. java自定义方法参数注解_Java方法中的参数太多,第1部分:自定义类型

    java自定义方法参数注解 我认为构造函数和方法中冗长的参数列表是Java开发中的另一个" 危险信号 ",就逻辑和功能而言,它们不一定是"错误的",但通常暗示当 ...

  8. Java方法中的参数太多,第1部分:自定义类型

    我认为构造函数和方法中冗长的参数列表是Java开发中的另一个" 危险信号 ",就逻辑和功能而言,它们不一定是"错误的",但通常暗示当前或将来出现错误的可能性很高 ...

  9. lua中的weak table及内存回收collectgarbage

    弱表(weak table)是一个很有意思的东西,像C++/Java等语言是没有的.弱表的定义是:Aweak table is a table whose elements are weak refe ...

最新文章

  1. BCP BIA DRP 灾后业务连续性方案简述
  2. [BZOJ1355][Baltic2009]Radio Transmission
  3. APP和网站应该选择云主机还是服务器呢?
  4. 二分枚举+贪心(nyist疯牛)
  5. python 打包自己得到的结果
  6. Lambda表达式练习1【应用】
  7. 导师带学生卡Bug,这波操作~
  8. tomcat高版本之URL解析异常解决
  9. python人口普查数据数据分析_2010年第六次人口普查数据分析
  10. 加拿大计算机科学专业高中选课,加拿大高中选课攻略
  11. 计算机休眠和睡眠省电,几步教会你笔记本睡眠和休眠有什么区别
  12. php 姓氏表,php 根据姓氏笔画排序怎么做
  13. mysql中查询一个商品价格最大的商品名称的sql出错记录
  14. 第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛
  15. adb shell 模拟器 关闭\打开WIFI
  16. 记录一次迁移 wss WebSocket 的事故
  17. 神经网络简介ppt英文,人工神经网络简介
  18. 【CSDN云IDE】个人使用体验和建议(含超详细操作教程)(python、webGL方向)
  19. springboot+vue前后端音乐网系统,挺漂亮的
  20. 使用maven-shade-plugin插件解决spark依赖冲突问题

热门文章

  1. USACO vans
  2. 初学c语言数据类型——变量
  3. factoryio虚拟工厂之智能仓储(简易)
  4. 动画基础2 -- 插值器与估值器
  5. C++ array使用方法详细介绍
  6. 通过代码来申请CA证书 -- cnblog
  7. SQL Server——触发器
  8. 几年了,我依然记得这故事的名字
  9. ks检验python代码_python scipy stats.kstest用法及代码示例
  10. vue项目搭建详细教程