Lua-UserData(用户自定义类型)

来源:互联网 发布:手机淘宝能看买家秀么 编辑:程序博客网 时间:2024/05/16 17:19

UserData(用户自定义类型)


意义:使用C语言编写的用于扩展Lua的新类型,方便使用脚本编写或者提高效率


userdata:提供了一块原始的内存区域,用于存储任何东西,在Lua中userdata没有任何预定义操作
生成:void *lua_newuserdata(L,size) 根据指定大小分配一块内存,并将userdata压入栈中,最后返回这个内存块的地址


例子:

Lua

require "array"a = array.new(1000)print(a);print(array.size(a))for i=1,1000 doarray.set(a,i,i%5 == 0)endprint(array.get(a,10))


C++

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <math.h>#include <limits.h>extern "C" {  #include "lua.h"#include "lauxlib.h"#include "lualib.h"} #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(L) (NumArray*)luaL_checkudata(L,1,"LuaBook.array")//优化typedef struct NumArray{int size;unsigned int value[1];}NumArray;//创建数组static int newarray(lua_State *L){int i,n;size_t nbytes;NumArray *a;n = luaL_checkint(L,1);//检查数组个数的参数是否正确luaL_argcheck(L,n>=1,1,"invalid size");nbytes = sizeof(NumArray) + I_WORD(n-1)*sizeof(unsigned int);a = (NumArray*)lua_newuserdata(L,nbytes);a->size = n;for(i=0;i<=I_WORD(n-1);i++){a->value[i] = 0;}//为所有新建的数组设置这个元表(优化)luaL_getmetatable(L,"LuaBook.array");lua_setmetatable(L,-2);return 1;//新的userdata已在栈上}//设置元素static int setarray(lua_State *L){NumArray *a = (NumArray*)checkarray(L);//优化int index = luaL_checkint(L,2) - 1;luaL_checkany(L,3);luaL_argcheck(L,a!=NULL,1,"'array' expected");luaL_argcheck(L,0<=index && index <a->size,2,"index out of range");if(lua_toboolean(L,3))a->value[I_WORD(index)] |= I_BIT(index);//设置bitelsea->value[I_WORD(index)] &= I_BIT(index);//重置bitreturn 0;}//获取元素static int getarray(lua_State *L){NumArray *a = (NumArray*)lua_touserdata(L,1);int index = luaL_checkint(L,2)-1;luaL_argcheck(L,a!=NULL,1,"array expected");luaL_argcheck(L,0<=index&&index<a->size,2,"index out of range");lua_pushboolean(L,a->value[I_WORD(index)]&I_BIT(index));return 1;}//获取长度static int getsize(lua_State *L){NumArray *a = (NumArray*)lua_touserdata(L,1);luaL_argcheck(L,a!=NULL,1,"array expected");lua_pushinteger(L,a->size);return 1;}//模块static const struct luaL_Reg arraylib[] = {{"new",newarray},{"set",setarray},{"get",getarray},{"size",getsize},{NULL,NULL}};//加载int luaopen_array(lua_State *L){luaL_newmetatable(L,"LuaBook.array"); //(优化)luaL_register(L,"array",arraylib);return 1;}//错误处理void err(lua_State* L,const char* fmt,...){va_list argp;va_start(argp,fmt);vfprintf(stderr,fmt,argp);va_end(argp);lua_close(L);//exit(EXIT_FAILURE);}int main(){lua_State* L = luaL_newstate();luaL_openlibs(L);luaopen_array(L);if(luaL_loadfile(L,"boolArray.lua") || lua_pcall(L,0,0,0))err(L,"cannot run config.lua:%s",lua_tostring(L,-1));getchar();return 0;}


优化userdata的定义:

原因:使用原来的方法,如果set的时候传入的是一个FILE*,由于判断是合法的,但实际上会修改到其他的内存,由于程序库不应该破坏C数据或在Lua中导致核心转储,所以需要进一步优化。


方法:辨别不同类型的userdata的方法是为每种类型创建一个唯一的元表


API:
int luaL_newmetatable(lua_State *L,const char *tname):创建一个新的table用作元表并压入栈顶,然后与注册表的指定名称关联起来(全局)
void luaL_getmetatable(L,tname):在注册表总检索tname的元表
void* luaL_checkudata(L,index,tname) :检查栈中指定位置上是否为一个userdata,存在则返回


面向对象的优化:

上面已经实现了userdata的定义,在lua中可以调用到指定的方法,但是不符合面向对象的访问习惯,这里做一下优化


当我们访问size()的时候不需要使用array.size(a)的方式,而是直接使用a:size()


方法:扩展原先定义的元表,设置__index元方法,这样就可以调用到指定的方法
local metaarray = getmetatable(array.new(1))
metaarray.__index = metaarray
metaarray.set = array.set
metaarray.size = array.size


修改注册方式:一个用于常规的函数,一个用于方法
static const struct luaL_Reg arraylib_f[] = 
{
{"new",newarray},
{NULL,NULL}
};
static const struct luaL_Reg arraylib_m[] = 
{
{"set",setarray},
{"get",getarray},
{"size",getsize},
{NULL,NULL}
};


修改luaopen_array:
int luaopen_array(lua_State *L){
luaL_newmetatable(L,"LuaBook.array"); //(优化)
lua_pushvalue(L,-1);
lua_setfield(L,-2,"__index");


luaL_register(L,NULL,arraylib_m);
luaL_register(L,"array",arraylib_f);
return 1;
}
在第一次调用中,以NULL作为库名,luaL_register不会创建任何用于存储函数的table,而是以栈顶的table作为存储函数的table


使用预定义的数组方式访问:修改注册方法
static const struct luaL_Reg arraylib_m[] = 
{
{"__index",setarray},
{"__newindex",getarray},
{"__len",getsize},
{NULL,NULL}
};
0 0
原创粉丝点击