• lua对象调用—用 "." 与 ":" 调用表中函数时的区别


    lua对象调用—用 "." 与 ":" 调用表中函数时的区别

    写这篇帖子之前,我看过许多关于绑定C++对象到Lua中的文章。总结一下他们的做法,用到元表、注册表、以及一些表中函数操作的一些基础知识以及相关的细节

    相信大家对Lua的表一点也不陌生,Lua表是个神奇的东西(本质上就是hash表),可以当做数组,可以当做map,还可用来模拟面向对象,这很Lua。
    我们在Lua中模拟面向对象的步骤中大家有没有仔细去研究过用 "." 与 ":" 调用表中函数时的区别呢,下面带大家一起来研究一下。

    例1

    [Lua] 例1
     1 --code 1:
     2 --定义一张表obj
     3 obj = {}
     4 --下面的这行代码等价于obj["func"]=function(a,b,c)...
     5 obj.func = function(a, b, c)
     6     print(a, b, c)
     7 end
     8  
     9 print("obj:", obj)
    10 obj.func(1,2)
    11 obj:func(1,2)

     输出:

    [Shell]

    obj: obj表地址
    1   2    nil
    obj表地址    1    2

    从上面的输出可以看出,当我们可以发现,当用"."来调用表内的函数时,与普通的函数调用没啥区别。

    但是当我们用":"来调用时,会把调用这个函数的那张表的传进去第一个参数中,后面的参数与实参一样。
    有了这个发现我们就可以继续往下探究。
    但是有一种情况是不会传入表到第一个参数的。看如下代码

    例2

    [Lua] 例2
    1 --下面这种定义表中函数的当大相信大家都不陌生
    2 obj = {}
    3 function obj:func(a, b, c)
    4     print(a, b, c)
    5 end
    6  
    7 print("obj:", obj)
    8 obj.func(1, 2)
    9 obj:func(1, 2)

    输出:

    [Shell]
    obj:obj表地址
    2    nil    nil
    1    2    nil

    发现了吧,当我们用如"function 表:函数名(......) .... end"的形式去定义一个表中的函数时,如果我们用"."来调用这个函数,就会得到莫名其妙的东西,这个现象不再我们的讨论之内;对于上面的定义方式,如果是用":"来调用那么跟调用普通的函数没啥区别。

    这个了解一下就好,因为我们用lua c api 操作表中函数时用的是上上面的那种定义形式。

    那么如果把这个obj表作为一个元表__index键的值再把这个元表与其他的表进行绑定,在触发__index时传的是哪张表进第一的参数呢
    有如下代码:

    例3

    [Lua] 例3
     1 obj = {}
     2 obj.func = function(a, b, c)
     3     print(a, b, c)
     4 end
     5  
     6 mtable = {__index = obj}
     7 otherobj = {}
     8  
     9 setmetatable(otherobj, mtable)
    10  
    11 print("obj:", obj)
    12 print("mtable", mtable)
    13 print("otherobj:", otherobj)
    14  
    15 otherobj.func(1, 2)
    16 otherobj:func(1, 2)

    输出:

    [Shell]
    obj:  obj表地址
    mtable:  mtable表地址
    otherobj:  otherobj表地址
    1    2    nil
    otherobj表地址    1    2

    从上面的结果可以知道,就算是在触发__index元方法调用的函数,如果是用":"来调用,那么一样会传入调用这个函数的那张表进第一个参数,就算是嵌套多层元表也是如此,大家感兴趣可以试一试嵌套多层元表,结果是一样的。

    元表还有一些小知识,就是__index键对应的是一个函数的时候也会把调用这个函数的表传进去,然后第二个参数是要调用的那个函数的名字,这时你就得手动处理是返回一个函数还是返回一个其他的量了。这个大家感兴趣也可以做做实验,这也是元表的基础知识。

    那么,如果这个函数时我们自己写的CFunction呢,还能这么如愿么,看下面的代码:

    例4

    [C] 例4
     1 //filename:funct.c
     2  
     3 #define LUA_LIB
     4 #include <lua.h>
     5 #include <lualib.h>
     6 #include <lauxlib.h>
     7  
     8 static int _c_l_testfunc(lua_State* L)
     9 {
    10     unsigned char argc, index;
    11     const char *typename;
    12     if ((argc = lua_gettop(L)) != 0) {
    13         printf("共传入 %d 个参数
    ", argc);
    14         for (index = 1; index <= argc; index++) {
    15             printf(
    16                 "第 %d 个参数类型为: %s
    ",
    17                 index, lua_typename(L, lua_type(L, index))
    18             );
    19         }
    20     } else {
    21         puts("0 个参数传入");
    22     }
    23      
    24     //清空栈
    25     lua_settop(L, 0);
    26      
    27     //把参数个数压入栈作为返回值
    28     lua_pushinteger(L, argc);
    29      
    30     return 1;
    31 }
    32  
    33 #if define(__cplusplus)
    34 #define EXP_FUNC __declspec(dllexport)
    35 #else
    36 #define EXP_FUNC
    37 #endif
    38  
    39  
    40 LUA_API EXP_FUNC int luaopen_funct(lua_State* L)
    41 {
    42     lua_pushcfunction(L, _c_l_testfunc);
    43     return 1;
    44 }

    上面的这个函数又来输出调用这个函数时的参数以及参数的类型。
    把上面的代码编译成动态库,我们来在lua中调用这个函数

    例5

    [lua] 例5
     1 funct = require "funct"
     2  
     3 --建一个表来存这个函数
     4 obj = {}
     5 obj.func = funct
     6  
     7 print(""."调用:")
     8 obj.func(1, "str")
     9 print("":"调用:")
    10 obj:func(1, "str")

    输出:

    [Shell]
    "."调用:
    共传入 2 个参数
    第 1 个参数类型为: number
    第 2 个参数类型为: string
    ":"调用
    共传入 3 个参数
    第 1 个参数类型为: table
    第 2 个参数类型为: number
    第 3 个参数类型为: string

    发现了吧,与lua内函数定义的函数传参没区别。如果大家半信半疑的话再把刚才那个C代码改一下,再添加一个把某个参数位置的值输出出来。这跑一下这个例子就明白了。现在告诉大家的是就算是CFunction,我们使用":"调用时也会把调用这个函数的表传进第一个参数,这使得我么把C++绑进lua中有方法可寻。


    还有一点,就是我们的userdata可以与元表绑定,lightuserdata不可以与元表绑定。如果我们在Lua中是对一个userdata进行":"调用的话,那么就会触发与之绑定的元表的__index方法,而且这回传进去的不是表了,是这个userdata,我们用lua_touserdata转换得到相应的对象指针。

    在Lua中,我们不能直接对userdata进行操作,但是我们可以间接的通过与之绑定的元表的__index进行操作。
    有一个点我们得注意的是,CFunction是普通成员函数指针类型,与类成员函数指针不一样,我们得再封装一层函数,这样子才能把函数绑定在我们的函数表中。
    大致思路:
    1.与类成员函数同名的CFunction,并且在其中完成对相应类成员函数的调用。
    2.把定义的CFunction打包到一张函数表methods中
    3.创建元表,设置键值对:__index = methods
    4.把这张元表绑定到userdata上(我们的对象指针)
    5.垃圾回收(主动回收和__gc被动回收)

    参考:https://www.52pojie.cn/thread-833988-1-1.html
    参考:解决C++成员变量在lua中直接使用的问题https://www.cnblogs.com/liao0001/p/9791557.html

  • 相关阅读:
    ASP.NET MVC中权限控制的简单实现
    HDU1004——Let the Balloon Rise
    如何使用飞秋FeiQ实现两电脑通信(或传输文件)
    vb.net 鼠标控制
    ireport制作报表pageheader只在第一页出现的解决办法
    Keycode对照表
    leetcode第一刷_Binary Tree Zigzag Level Order Traversal
    换硬币问题
    STM32 寄存器库和固件库
    java网络编程(2)InetAddress 类及udp协议
  • 原文地址:https://www.cnblogs.com/lzpong/p/13426782.html
Copyright © 2020-2023  润新知