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

};

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-07 13:17:11

Lua-UserData(用户自定义类型)的相关文章

Lua学习之类型与值

Lua是一种动态语言,在语言中没有类型定义的语法. 在lua中有8中基本的类型: 1.nil(空) 2.boolean 3.number(数字) 4.string(字符串) 5.userdata(自定义类型) 6.function(函数) 7.thread(线程) 8.table(表) 函数type可根据一个值返回类型的名称. print(type("Hello world")) ------------->string 由于变量是没有预定义类型的,所以 a = 10; prin

Lua中的类型与值

[基础介绍] Lua是一种动态类型的语言.在语言中没有类型定义的语法,每个值都带有其自身的类型信息.在Lua中有8中基本类型,分别是: nil(空)类型 boolean(布尔)类型 number(数字)类型 string(字符串)类型 userdata(自定义类型) function(函数)类型 thread(线程)类型 table(表)类型 以上是Lua中的8中基本类型,我们可以使用type函数,判断一个值得类型,type函数返回一个对应类型的字符串描述.例如: local iValue =

用户自定义类型

userdata: userdata机制可以让我们在lua中使用c中的自定义数据类型.userdata表示一块动态分配的内存,这块内存就存储的自定义类型的数据,在lua脚本中使用userdata,并配合c提供的函数,就可以操作userdata了. 定义一个player类型: typedef struct _Player { int id; char name[20]; int account; } Player; 定义player的所有操作: static int _index = 1; sta

lua学习之类型与值篇

类型与值 lua 是动态类型的语言 在语言中没有类型定义的语法 每个值都携带有它的类型信息 8种基础类型 用 type 可以返回这个值的类型的名称 将一个变量用于不同类型,通常会导致混乱的代码 但合理使用,如异常情况下返回 nil ,以区别正常情况下的其他类型的返回值 变量没有预定义的类型,任何变量都可以包含任何类型的值 print(type("Hello")) --string print(type(666)) --number a = print print(type(a)) --

边做边学Rust之用户自定义类型

3 用户自定义类型 Rust自定义类型主要通过下面两个关键进行定义: struct:定义一个结构 enum:定义一个枚举 常量能以通过const和static关键字创建. 3.1 结构 有三种类型的结构("structs"),可以使用struct关键字来创建: 元组结构体,又名元组 传统C结构体 元结构体,无field,为了做成通用类型 // A unit struct struct Nil; // A tuple struct struct Pair(i32, f64); // A

C++的内置类型和用户自定义类型的互相转换

这两种之间的转换主要有两种方式第一就是内置类型转换为用户自定义类型,这里以int类型转换为类类型的对象为例,第二就是类类型转换为int类型 前者依靠转换构造函数,后者依靠自定义的类型转换函数 一.转换构造函数 二.类型转换构造函数 #include <iostream> using namespace std; //转换构造函数只有一个参数,但是有时候这种一个参数的构造函数不仅起到类型转换的作用也起到了构造函数的作用,但是只有一个参数的构造函数才有这种类似的双重身份,并且这种没有拷贝构造的情况

0914,异常语句,类类型(用户自定义类型),分割

异常语句try :尝试 try { //要包括起来的可能有错误的代码 } catch (Exception ex)//抓获错误 { throw ex;  //抛出错误异常 console.writeline ("错误内容"+ex ); } finally { console.write(""); } 类   类型 : class类型 : 用户自定义类型 String :   处理字符串 DateTime : 处理时间 Random :   生产随机数 Math : 

c/c++_Lua交互----关于Lua中table类型的使用实例

lua中的复合类型 只有table 类型,你可以当做任意容器使用  ,比如 数组    PHP中的关联数组  C++中的 std::map 等等  而且提供了很方便的使用 下面是lua中 table类型的使用 c++加载代码 #include "string.h" extern "C" { #include "lualib.h" //包含lua lib #include "lauxlib.h" //辅助函数 }; #pragm

使用用户自定义类型作为map的key

有时候我们想把用户自定义类型作为std::map的键值.方法一)最简单的方法就是实现该自定义类型的<操作符,代码如下:class Foo{public:    Foo(int num_)        : num(num_)    {    }    bool operator < (const Foo & cmp) const    {        return num < cmp.num;    }      int num;   };之后就可以使用Foo作为map的key