UserData(用户自定义类型)
意义:使用C语言编写的用于扩展Lua的新类型,方便使用脚本编写或者提高效率
例子:
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
#include
#include
#include
#include
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 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&&indexsize,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;
}
原因:使用原来的方法,如果set的时候传入的是一个FILE*,由于判断是合法的,但实际上会修改到其他的内存,由于程序库不应该破坏C数据或在Lua中导致核心转储,所以需要进一步优化。
方法:辨别不同类型的userdata的方法是为每种类型创建一个唯一的元表
上面已经实现了userdata的定义,在lua中可以调用到指定的方法,但是不符合面向对象的访问习惯,这里做一下优化
当我们访问size()的时候不需要使用array.size(a)的方式,而是直接使用a:size()