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 do array.set(a,i,i%5 == 0) end print(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); //设置bit else a->value[I_WORD(index)] &= I_BIT(index); //重置bit return 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}
};
版权声明:本文为博主原创文章,未经博主允许不得转载。