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

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 
#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;
}


优化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}
};

你可能感兴趣的:(Lua)