• 用户自定义类型《lua程序设计》 28章 笔记


    本实例实现一种很简单的类型------布尔数组。C语言可以实现将每个布尔值存储在一个bit中,从而减少内存用量。

    必须的一些宏

    Code Snippet
    1. #defineBITS_PER_WORD (CHAR_BIT * sizeof(unsignedint))  //bit
    2. #defineI_WORD(i) ((unsignedint) i / BITS_PER_WORD)    //bitword
    3. #defineI_BIT(i) 1<<((unsignedint) (i) % BITS_PER_WORD) //word bit

    lua函数  lua_newuserdata
    该函数会根据指定大小分配一块内存,并将对应的userdata压入栈中最后返回这个内存块的地址。

    以下函数用lua_newuserdata创建一个新的布尔数组

    以下函数用lua_newuserdata创建了一个新的布尔数组

    Code Snippet
    1. typedefstructNumArray{
    2.     intsize;
    3.     unsignedintvalues[1];
    4. }NumArray;
    5.  
    6. staticintnewarray(lua_State * L)
    7. {
    8.     
    9.     size_tnBytes;
    10.     NumArray * a;
    11.     intn = luaL_checkint(L,1);   //
    12.     luaL_argcheck(L,n>=1,1,"invalid size");  //>1
    13.     nBytes = sizeof(NumArray) + I_WORD(n-1)*sizeof(unsignedint);
    14.     a = (NumArray * ) lua_newuserdata(L,nBytes);
    15.     a->size = n;
    16.     for(inti = 0;i<I_WORD(n-1);i++)
    17.         a->values[i] = 0;//
    18.     return 1;
    19. }

    只要lua注册好newarray就可以通过 a = array.new(1000) 来创建一个数组。array.set(array,index,value)在数组中存储元素。

    Code Snippet
    1. staticintsetarray(lua_State * L)
    2. {
    3.     NumArray * a = (NumArray * ) lua_touserdata(L,1);
    4.     intindex = luaL_checkint(L,2);
    5.     luaL_checkany(L,3);
    6.  
    7.     if(lua_toboolean(L,3))
    8.         a->values[I_WORD(index)] |= I_BIT(index);
    9.     else
    10.         a->values[I_WORD(index)] |= I_BIT(index);
    11.     return 0;
    12. }

    对应的有getarray

    Code Snippet
    1. staticintgetarray(lua_State * L)
    2. {
    3.       NumArray * a= (NumArray * ) lua_touserdata(L,1);
    4.     intindex = luaL_checkint(L,2);
    5.     lua_pushboolean(L,a->values[I_WORD(index)] & I_BIT(index));
    6.     return 1;
    7. }

    下面还定义了一个函数用于检索一个数组大小

    Code Snippet
    1. staticintgetsize(lua_State * L)
    2. {
    3.     NumArray * a = (NumArray*) lua_touserdata(L,1);
    4.     lua_pushinteger(L,a->size);
    5.     return 1;
    6. }

    最后需要代码来初始化这个库

    Code Snippet
    1. staticconststructluaL_Regarraylib[] =
    2. {
    3.     {"new",newarray},
    4.     {"set",setarray},
    5.     {"get",getarray},
    6.     {"size",getsize},
    7.     {NULL,NULL}
    8. };
    9.  
    10. intluaopen_array(lua_State * L)
    11. {
    12.     luaL_register(L,"array",arraylib);
    13.     return 1;
    14. }

    28.2 元表

    一种辨别不 同类型的userdata的方法是,为每种类型创建一个唯一的过元表,每当创建一个userdata后,就用相应的元表来标记它。每当和到一个userdata之后,就检查它是否拥有正确元表。由于lua代码不能改变usedata的元表,因此也无法欺骗代码。

    通常辅助库提供了一些函数来实现这些内容,可以使用的辅助库函数有

    int luaL_newmetatable(lua_State * L,const char * name)  创建一个新的table用作元表,并将其压栈,然后将这个table与注册表中的指定名称关联起来

    int luaL_getmetatable(lua_State * L ,const char * name)  可以在注册表中检测与name关联的元表

    void * luaL_checkudata(lua_State * L ,int index,const char * name) 可以检查栈中指定位置上是否有一个userdata,或者它不具有正确名称,就会引发一个错误。否则就返回这个usrdata的地址。

    下面开始修改代码 :

    1.必须创建一元表

    Code Snippet
    1. intluaopen_array(lua_State * L)
    2. {
    3.     luaL_newmetatable(L,"LuaBook.array");
    4.     luaL_register(L,"array",arraylib);
    5.     return 1;
    6. }


      2.修改newarray 使其为所有新建的数组设置这个元表

    Code Snippet
    1. staticintnewarray(lua_State * L)
    2. {
    3.     
    4.     size_tnBytes;
    5.     NumArray * a;
    6.     intn = luaL_checkint(L,1);   //
    7.     luaL_argcheck(L,n>=1,1,"invalid size");  //>1
    8.     nBytes = sizeof(NumArray) + I_WORD(n-1)*sizeof(unsignedint);
    9.     a = (NumArray * ) lua_newuserdata(L,nBytes);
    10.     a->size = n;
    11.     for(inti = 0;i<I_WORD(n-1);i++)
    12.         a->values[i] = 0;//
    13.  
    14.     luaL_getmetatable(L,"LuaBook.array");
    15.     lua_setmetatable(L,-2);
    16.     return 1;
    17. }

    3.最后,setarray getarray getzie 必有检其第一个参数是不回为一个合法的数组。所以定义了如下宏

    Code Snippet
    1. #definecheckarray(L) \par     (NumArray*) luaL_checkudata(L,1,"LuaBook.array")

    28.3 面向对象的访问

    下一步就是这种新类型变换为一个对象,然后就可以用普面向对象的语法来使用它们了如

    a = array.new(1000)
    print(a:size())  --1000
    a:set(10,true) 
    print(a:get(10)) –true

    注意:a: size() 等价于a.size(a)  因此,必须使用表达式a.size返回前面定义的函数getsize。实现这一点的关键是使用__index元方法。对于table而言Lua会在找不到指定key时调用 这个,对于userdata,则会在每次访问都调用它。假设运行如下代码

    local metaarray = getmetatable(array.new(1)) --创建一个数组
    metaarray.__index = metaarray 
    metaarray.set = array.set
    metaarray.get = array.get
    metaarray.size = array.size

    第一行创建了数组,并将它的元表赋予metaarray。然后将metaarray.__index设置为metaarray。当对size求值时,lua会尝试通过a的元表__index字段来查这个值最后就找到了array.size()

    其实,在c中也可以达到相同的效果,甚至还可以做的更好现在数组是一种具有操作的对象,可以无须在table array中保存这些操作。程序库只要导出一个用于创建新数组函数new就可以了。所有其它操作都可以作为对象的方法。c代码同样可以直接注册这些方法。操作getsize,getarray和setarray无须作任何改变 唯一改变的是注册它们的方式 。首先需要设置两个独立的函数列表,一个用于常规函数,一个用于方法

    Code Snippet
    1. staticconststructluaL_Regarraylib_f[] =
    2. {
    3.     {"new",newarray},
    4.     {NULL,NULL}
    5. };
    6.  
    7.  
    8. staticconststructluaL_Regarraylib_m[] =
    9. {
    10.     {"set",setarray},
    11.     {"get",getarray},
    12.     {"size",getsize},
    13.     {NULL,NULL}
    14. };

    新的打开函数luaopen_array必须创建过元表,并将它赋予__index字段,然后在元表中注册所有方法,最后创建并填充array table。

    Code Snippet
    1. intluaopen_array(lua_State * L)
    2. {
    3.     luaL_newmetatable(L,"LuaBook.array");
    4.     /*.__index = */
    5.     lua_pushvalue(L,-1); //
    6.     lua_setfield(L,-2,"__index");
    7.  
    8.     luaL_register(L,NULL,arraylib_m);
    9.     luaL_register(L,"array",arraylib_f);
    10.  
    11.     return 1;
    12. }

    其中用到了 luaL_register的另一个特性。在第一次调用中,以NULL作为库名,luaL_register不会创建任何用于存储函数的table,而是以栈顶的table作用存储函数的table。在本本例中栈顶table就是元表本身,因此lua_register会将所有方法放入其中,第二次调用luaL-register则提供了一个表名,它就根据此名倒地 一个新的table,并将指定的函数注册在这个table中。

    28.4 数组的访问

    另一种写法是常规的数组写法 a[i]  。由于函数setarray和getarray所接受的参数次序暗剑相应元方法的次序。因此在lua代码中可以快速的将这些元方法定义为。

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

    如果还要更完美,可以在c代码中注册这些方法,为此需要再次修改初始化函数

    Code Snippet
    1. staticconststructluaL_Regarraylib_f[] =
    2. {
    3.     {"new",newarray},
    4.     {NULL,NULL}
    5. };
    6.  
    7.  
    8. staticconststructluaL_Regarraylib_m[] =
    9. {
    10.     {"__newindex",setarray},
    11.     {"__index",getarray},
    12.     {"__len",getsize},
    13.     {NULL,NULL}
    14. };
    Code Snippet
    1. intluaopen_array(lua_State * L)
    2. {
    3.     luaL_newmetatable(L,"LuaBook.array");
    4.  
    5.     luaL_register(L,NULL,arraylib_m);
    6.     luaL_register(L,"array",arraylib_f);
    7.  
    8.     return 1;
    9. }

    28.5 轻量级userdata (light userdata)

    之前介绍的是full userdata,Lua还提供了另一种轻量级userdata(light userdata)。事实上,轻量级userdata仅仅表示的是C指针的值,即(void*)。由于它只是一个值,所以不用创建。如果需要将一个轻量级userdata放入栈中,调用lua_pushlightuserdata即可。full userdata和light userdata之间最大的区别来自于相等性判断,对于一个full userdata,它只是与自身相等,而light userdata则表示为一个C指针,因此,它与所有表示同一指针的light userdata相等。再有就是light userdata不会受到垃圾收集器的管理,使用时就像一个普通的整型数字一样

  • 相关阅读:
    Android Studio安装apk失败
    react-native获取屏幕尺寸
    Project Euler Problem 10
    Project Euler Problem9
    Project Euler Problem8
    Project Euler Problem7
    Project Euler Problem6
    《The One 团队》:第九次团队作业:BETA冲刺与团队项目验收
    《The One!团队》:BETA Scrum metting3
    《The One !团队》:BETA Scrum metting2
  • 原文地址:https://www.cnblogs.com/zhangdongsheng/p/3657787.html
Copyright © 2020-2023  润新知