Lua C用户自定义类型

转自lua程序设计第二版

userdata

userdata提供一块原始的内存区域,可以用来存储任何东西。
lua中userdata没有任何预定义的操作。

//根据指定的大小分配一块内存,并将对应的userdata压入栈中,最后返回这个内存块的地址
void *lua_newuserdata(lua_State *L,size_t size);

元表

一种辨别不同类型的userdata的方法时,为每种类型创建一个唯一的元表。每当创建了一个userdata后,就用相应的元表来标识它。

在Lua中,通常习惯是将所有新的C类型注册到注册表中,以一个类型名为key,元表作为value。

//创建一个新的table用作元表,并将其压入栈顶,然后将这个table与注册表中的指定名称关联起来。
int luaL_newmetatable(lua_State *L,const char* tname);
//在注册表中检索与tname相关联的元表。
int luaL_getmetatable(lua_State *L,const char* tname);
//检查栈中指定位置上是否为一个userdata,并且是否具有与给定名称相匹配的元表。
//如果该对象不是一个userdata,或者它不具有正确的元表,就会引发一个错误,否则就返回这个userdata的地址
void *luaL_checkudata(lua_State *L,int index,const char* tname);

实例布尔数组

#include <limits.h>
//CHAR_BIT: number of bits in byte = 8
//一个int占用多少位
#define BITS_PER_WORD (CHAR_BIT * sizeof(unsigned int))
//一个长度为n的布尔数组需要多少个int : I_WORD(1000) * sizeof(unsigned int)
#define I_WORD(i) ((unsigned int)(i) / BITS_PER_WORD)
#define I_BIT(i)) (1<<((unsigned int)(i) % BITS_PER_WORD))

//数组结构
typedef struct NumArray{
    int size;
    //C 89不允许分配0长度的数组
    unsigned int values[1];
}

//计算具有n个元素的数组大小
//sizeof(NumArray) + I_WORD(n-1)*sizeof(unsigned int)

//用lua_newuserdata创建一个新的布尔数组
static int newarray(lua_State *L){
    int i,n;
    size_t nbytes;
    NumArray *a;

    //只是在luaL_checkinteger后进行一个类型转换
    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->values[i] = 0;
    }

    luaL_getmetatable(L,"LuaBook.array");
    //lua_newmetatable从栈中弹出一个table,并将其设为指定索引上对象的元表
    lua_setmetatable(L,-2);

    //新的userdata已在栈中
    return 1;
}

//只要在lua中注册好newarray,就可以通过a = array.new(1000)来创建数组

//存储元素
static int setarray(lua_State *L){
    NumArray* a = (NumArray*)lua_touserdata(L,1);
    int index = luaL_checkint(L,2) - 1;

    //lua中任何值都可以转换为布尔,luaL_checkany只是确保了在这个参数位置上游一个值
    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)){
        //设置bit
        a->values[I_WORD(index)] |= I_BIT(index);
    }else{
        //重置bit
        a->values[I_WORD(index)] &= ~I_BIT(index);
    }
}

//检索元素
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->values[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){
    lua_newmetatable(L,"LuaBook.array");
    lua_register(L,"array",arraylib);
    return 1;
}
a = array.new(1000)
print(a)
print(array.size(a))  ---> 1000
for i = 1, 1000 do
    array.set(a,i,i%5 == 0)
end
print(array.get(a,10)) ---> true

假设用户写了这样的语句array.set(io.stdin,1,false)
io.stdin是一个userdata,是一个文件流指针(FILE*)。由于这是一个userdata,array.set会认为它是一个合法的参数,结果就使内存遭到破坏(如果幸运,可能会得到一个索引超出范围的错误)。但对于有些lua库来说,是不可接受的。

面向对象访问

a:size() <==> a.size(a)
必须使a.size返回前面定义的getsize,使用–index元方法
对于table而言,Lua会在找不到指定key时调用这个元方法
对于userdata,则在每次访问都调用它,因为userdata根本没有key

lua实现

--[[
创建一个数组,并将它的元表赋予了metaarray,然后将metaarray.__index设为metaarray.
当对a.size求值时,由于a是一个userdata,所以Lua无法再对象a中找到key"size",一次Lua会尝试通过
a的元表__index字段查找这个值,也就是metaarray本身,由于metaarray.size为array.size.
因此a.size(a)结果就是array.size(a)
]]
local metaarray = getmetatable(array.new(1))
metatable.__index = metaarray
metaarray.set = array.set
metaarray.get = array.get
metaarray.size = array.size

C中实现

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

int luaopen_array(lua_State *L){
    luaL_newmetatable(L,"LuaBook.array");

    lua_pushvalue(L,-1);
    lua_setfield(L,-2);
    //以NULL作为库名,luaL_register不会创建任何用于存储函数的table,而是以栈顶的table作为存储函数的table。栈顶table就是元表本身
    luaL_register(L,NULL,arraylib_m);
    //提供库名,luaL_register根据此名创建了一个新的table,并将指定的函数注册在这个table中(也就是本例中唯一的new函数)
    luaL_register(L,"array",arraylib_f);
    return 1;
}

数组访问

a:get(i) <==> a[i]

Lua实现

local metaarray = getmetatable(array.new(1))
metaarray.__index = array.get
metaarray.__newindex = array.set
metaarray.__len = array.size

C实现

//在static const struct luaL_Reg arraylib_m[]中添加
{"__newidnex",setarray},
{"__index",getarray},
{"__len",getsize},
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值