lua绑定C++对象系列一——基础知识

本文主要介绍lua绑定C++对象的原理和方法,并能在C/C++定义类和方法,在lua中创建C++类的句柄实例,像面向对象一样去使用C++类实例。为了便于大家理解,系列文章会从基础知识讲解,并通过多个版本的进化,一步步完成从基础到多版本实践的完美结合和深入,彻底理解lua绑定C++对象的原理方法。在阅读本系列文章前,需要具备一定的lua开发经验以及lua与C/C++相互调用操作的知识。

1、基础C/C++和Lua的相互引用调用

  我们知道C和lua相互调用,是通过虚拟栈进行数据传递通信的,基础介绍介绍就不在这里赘述。这里介绍一个C函数print_stack和一个lua函数print_tree。

  • print_stack

  它能够打印出stack当前的状态,方便使用的过程进行调试,我们知道lua虚拟栈是push数据是从下往上的,最顶上的的index为-1,下面的代码是从最顶往下打印。

  基本代码文件:comm.h

 1 #include <iostream>
 2 #include <cstring>
 3 #include <stdlib.h>
 4 extern "C" {
 5 #include <lua.h>
 6 #include <lualib.h>
 7 #include <lauxlib.h>
 8 }
 9
10 using namespace std;
11
12 /*
13  * #define LUA_TNIL     0
14  * #define LUA_TBOOLEAN     1
15  * #define LUA_TLIGHTUSERDATA   2
16  * #define LUA_TNUMBER      3
17  * #define LUA_TSTRING      4
18  * #define LUA_TTABLE       5
19  * #define LUA_TFUNCTION        6
20  * #define LUA_TUSERDATA        7
21  * #define LUA_TTHREAD      8
22  * */
23
24 char* get_val(lua_State *L, int idx)
25 {
26     static char sData[32];
27     sData[0] = ‘\0‘;
28
29     int type = lua_type(L, idx);
30     switch (type)
31     {
32         case 0: //nil
33             {
34             snprintf(sData, sizeof(sData), "%s", "nil");
35             break;
36             }
37         case 1://bool
38             {
39             int val = lua_toboolean(L, idx);
40             snprintf(sData, sizeof(sData), "%s", val == 1 ? "true" : "false");
41             break;
42             }
43         case 3://number
44             {
45             double val = lua_tonumber(L, idx);
46             snprintf(sData, sizeof(sData), "%f", val);
47             break;
48             }
49         case 4://string
50             {
51             const char* val = lua_tostring(L, idx);
52             snprintf(sData, sizeof(sData), "%s", val);
53             break;
54             }
55         case 2:
56         case 5:
57         case 6:
58         case 7:
59         case 8:
60         default:
61             {
62             const void* val = lua_topointer(L, idx);
63             snprintf(sData, sizeof(sData), "%p", val);
64             break;
65             }
66
67     }
68
69     return sData;
70 }
71
72 int print_stack(lua_State *L)
73 {
74     int iNum = lua_gettop(L);
75     cout<<"==========Total:"<<iNum<<"=========="<<endl;
76     for (int i = iNum; i >= 1; i--)
77     {
78         int idx = i - iNum - 1;
79         int type = lua_type(L, i);
80         const char* type_name = lua_typename(L, type);
81         cout<<"idx:"<<idx<<" type:"<<type<<"("<< type_name<<") "<<get_val(L, i)<<endl;
82     }
83     cout<<"==========================="<<endl;
84     return 0;
85 }

打印效果如下:

==========Total:3==========
idx:-1 type:0(nil) nil
idx:-2 type:0(nil) nil
idx:-3 type:5(table) 0x11251c0
===========================
  • print_tree

  lua函数print_tree能够打印table结构,也是为了方便查看table的层次数据。

  基本代码文件: tree.lua

 1 function print_tree(var, depth)
 2     print(var)
 3     local bitmap = {}
 4     function print_tree_i(var, depth)
 5         if type(var) ~= "table" then
 6             print("not a table");
 7             return
 8         end
 9
10         local depth = depth or 0
11         local tab = string.rep("        ", depth);
12         depth = depth  + 1
13
14         if depth >= 4 then
15             return
16         end
17
18         bitmap[var] = true
19
20         for k,v in pairs(var) do
21             if type(v) ~= "table" then
22                 print(string.format("%s%-7s %s", tab, tostring(k), tostring(v)))
23             else
24                 if not bitmap[v] then
25                     print(string.format("%s%s(%s)", tab, tostring(k), v))
26                     print_tree_i(v, depth)
27                 else
28                     print(string.format("%s%-7s %s+", tab, tostring(k), v))
29                 end
30             end
31         end
32         return
33     end
34
35     print_tree_i(var, depth)
36 end
37
38 function print_metatable(tab)
39     if type(getmetatable(tab)) ~= "table" then
40         print("has no metatable");
41         return
42     end
43
44     print_tree(getmetatable(tab));
45 end

打印效果如下:

print_tree({[1]=1,[2]=2,[3]=3,[4]=4,[5]=5,[6]=6,[7]=7,[8]=8,[9]=9,[10]=10,[11]={hello="worldha", hello1="worldha"}})

1       1
2       2
3       3
4       4
5       5
6       6
7       7
8       8
9       9
10      10
11(table: 0x201f200)
hello   worldha
hello1  worldha

2、几个面向对象重要的接口介绍

  在通过lua绑定C++对象时,常用的接口有以下几个lua_register、lua_getgloba/lua_setglobal、lua_setfield(L, LUA_REGISTRYINDEX, "xxx")/lua_getfield(L, LUA_REGISTRYINDEX, "xxx")、_G、luaL_newmetatable/luaL_getmetatable,他们调用过程中数据数据存放在哪里,对lua的底层数据结构有什么影响?

  针对lua5.3.4,lua虚拟机针对每个进程有个lua_State私有数据,而这些进程共享一个全局数据global_State。global_State中有一个l_registry注册表,这是一个预定义出来的表,可以用来保存任何代码想保存的 Lua 值。 这个表可以用有效伪索引 LUA_REGISTRYINDEX来定位,当然全局数据也是放在里面。具体的l_registry结构如下:

  如上图可以看到,l_registry的index为1指向lua_State对象,index为2指向global表,而所有的库都是初始化到这个表中。下面分情况说明一下:

  • 当我们在lua中使用print或者io.open时,相当于是引用l_registry[2] [“print”]和 l_registry[2][“io”][“open”]元素
  • 当我们在lua中定义全局函数print_tree时,相当于写入元素l_registry[2][“print_tree”]
  • 当我们在代码中使用_G.print_tree和print_tree时,实际是引用l_registry[2][“_G”][“print_tree”] 和 l_registry[2][“print_tree”],两者实际等价。因为l_registry[2][“_G”] = l_registry[2],相当于引用自身。
  • luaL_getmetatable/luaL_newmetatable是操作l_registry这个表。当我们使用luaL_getmetatable(L, tabname)进行查找时,实际是在查找l_register[tabname]是否存在。当调用luaL_newmetable(L, tabname)时首先判断l_register[tabname]是否存在,存在返回0.不存在就创建l_register[tabname] = {__name=tabname},并返回1。一种类型的C++对象,元表是一样的,可以共享元表定义,不用每个对象自己单独创建元表。所以使用luaL_newmetatable()会比较合适。

C/C++中调用print_stack():

1 lua_getglobal(L, "print_tree");
2 lua_pushinteger(L, 2);
3 lua_gettable(L, LUA_REGISTRYINDEX); //将l_registry[2]推入栈中
4 lua_pcall(L, 1, 0, 0); //等价于print_tree(l_register[2])

打印结果如下:

table: 0x1d5a930  //l_registry[2]的地址
select  function: 0x425589
require function: 0x1d5cb60
rawget  function: 0x424cd3
rawlen  function: 0x424c62
dofile  function: 0x425453
table(table: 0x1d5cf30)
        maxn    function: 0x42f46a
        move    function: 0x42f75d
        sort    function: 0x43027c
        insert  function: 0x42f536
        unpack  function: 0x42fbec
        concat  function: 0x42fa06
        pack    function: 0x42fb4a
        remove  function: 0x42f657
hello   nihao
assert  function: 0x4254f2
os(table: 0x1d5cea0)
        rename  function: 0x42a518
        time    function: 0x42ac8c
        remove  function: 0x42a4ca
        tmpname function: 0x42a588
        clock   function: 0x42a660
        exit    function: 0x42aef3
        getenv  function: 0x42a61d
        setlocale function: 0x42ae77
        date    function: 0x42aa3c
        execute function: 0x42a45e
        difftime function: 0x42ae09
pcall   function: 0x4256bc
getmetatable function: 0x424aed
loadfile function: 0x42515e
rawequal function: 0x424c07
print_tree function: 0x1d61390  //print_tree是自己定义的全局函数
_VERSION Lua 5.3
rawset  function: 0x424d2f
setmetatable function: 0x424b4f
tonumber function: 0x4248da
tostring function: 0x4257f8
module  function: 0x1d5caf0
bit32(table: 0x1d60740)
        bxor    function: 0x4259f0
        arshift function: 0x425bbb
        bnot    function: 0x425a59
        rshift  function: 0x425b6a
        extract function: 0x425e2d
        band    function: 0x425915
        rrotate function: 0x425d34
        bor     function: 0x425987
        lshift  function: 0x425b1c
        lrotate function: 0x425d03
        btest   function: 0x42594b
        replace function: 0x425eb4
pairs   function: 0x425022
print_tree_i function: 0x1d5de50
print_metatable function: 0x1d60ff0
debug(table: 0x1d5aab0)
        getlocal function: 0x426c2d
        getinfo function: 0x426893
        gethook function: 0x427564
        traceback function: 0x4277f5
        getmetatable function: 0x4265ca
        debug   function: 0x4276b0
        sethook function: 0x42736d
        setlocal function: 0x426dd9
        setmetatable function: 0x42660f
        setupvalue function: 0x426ffb
        setuservalue function: 0x4266c7
        getupvalue function: 0x426fdc
        getuservalue function: 0x42667f
        upvaluejoin function: 0x4270f2
        getregistry function: 0x4265a6
        upvalueid function: 0x4270a0
io(table: 0x1d5d230)
        flush   function: 0x429055
        write   function: 0x428e5b
        input   function: 0x428043
        output  function: 0x428067
        stderr  file (0x7fedf80321c0)
        popen   function: 0x427e22
        close   function: 0x427b64
        stdin   file (0x7fedf8032640)
        lines   function: 0x428155
        stdout  file (0x7fedf8032400)
        read    function: 0x428afe
        type    function: 0x427993
        tmpfile function: 0x427ed1
        open    function: 0x427d0f
math(table: 0x1d5c050)
        atan2   function: 0x42952f
        floor   function: 0x429695
        asin    function: 0x42947d
        random  function: 0x429dfd
        randomseed function: 0x429f79
        acos    function: 0x4294d6
        ult     function: 0x429a54
        deg     function: 0x429c63
        fmod    function: 0x4297a7
        max     function: 0x429d6c
        log     function: 0x429ab0
        maxinteger 9223372036854775807
        min     function: 0x429cdb
        atan    function: 0x42952f
        tointeger function: 0x4295c3
        exp     function: 0x429c0a
        sin     function: 0x429372
        pi      3.1415926535898
        huge    inf
        mininteger -9223372036854775808
        ceil    function: 0x42971e
        cosh    function: 0x42a02f
        modf    function: 0x4298e8
        frexp   function: 0x42a1c6
        sqrt    function: 0x4299fb
        cos     function: 0x4293cb
        ldexp   function: 0x42a23b
        abs     function: 0x4292e0
        log10   function: 0x42a2b5
        tan     function: 0x429424
        tanh    function: 0x42a0e1
        sinh    function: 0x42a088
        pow     function: 0x42a13a
        type    function: 0x429fae
        rad     function: 0x429c9f
string(table: 0x1d5bd90)
        lower   function: 0x42b1fb
        match   function: 0x42cbfa
        len     function: 0x42afe8
        pack    function: 0x42e634
        rep     function: 0x42b363
        upper   function: 0x42b2af
        packsize function: 0x42ec59
        char    function: 0x42b670
        unpack  function: 0x42ee87
        gsub    function: 0x42d173
        byte    function: 0x42b520
        format  function: 0x42db29
        reverse function: 0x42b153
        gmatch  function: 0x42ccf4
        sub     function: 0x42b064
        find    function: 0x42cbdb
        dump    function: 0x42b77a
collectgarbage function: 0x424d9c
utf8(table: 0x1d5fed0)
        codepoint function: 0x43066d
        codes   function: 0x430c2f
        char    function: 0x43088d
        offset  function: 0x430934
        charpattern [-[*
        len     function: 0x430500
unpack  function: 0x42fbec
print   function: 0x424628
next    function: 0x424fc0
coroutine(table: 0x1d5cbd0)
        wrap    function: 0x426326
        resume  function: 0x426170
        status  function: 0x42638d
        create  function: 0x4262c4
        yield   function: 0x42635b
        isyieldable function: 0x426495
        running function: 0x4264c4
ipairs  function: 0x4250aa
_G      table: 0x1d5a930+ //l_registry[2][“_G”]指向地址就是l_registry[2]
error   function: 0x424a5a
loadstring function: 0x4252fa
xpcall  function: 0x42574c
load    function: 0x4252fa
type    function: 0x424e8b

  通过上述的分析,可以很清楚的解释第2点的关系了:

  • l_registry[2]、_G、全局表几个概念等价
  • lua_register只是把c函数注册到全局table,即注册到l_registry[2]中
  • lua_setglobal和lua_getglobal只是修改和查询全局表,即l_registry[2]这个表
  • lua_setfield(L, LUA_REGISTRYINDEX, "xxx")/lua_getfield(L, LUA_REGISTRYINDEX, "xxx")只是修改更上层的l_registry这个表,可以用来保存C/C++代码想保存的lua值。
  • luaL_newmetatable/luaL_getmetatable底层调用lua_setfield(L, LUA_REGISTRYINDEX, "xxx")/lua_getfield(L, LUA_REGISTRYINDEX, "xxx"),修改l_registry这个表

3、几种面向对象中常见的元方法介绍

下面介绍的几个元方法,在后面lua绑定C++对象的时候会频繁使用,所以这里进行一下简单的介绍:

__index: 索引 table[key]。 当 table 不是表或是表 table 中不存在 key 这个键时,这个事件被触发。此时,会读出 table 相应的元方法。

尽管名字取成这样,这个事件的元方法其实可以是一个函数也可以是一张表。如果它是一个函数,则以 table 和 key 作为参数调用它。如果它是一张表,最终的结果就是以 key 取索引这张表的结果

__newindex: 索引赋值 table[key] = value 。 和索引事件类似,它发生在 table 不是表或是表 table 中不存在 key 这个键的时候。此时,会读出 table 相应的元方法。

同索引过程那样,这个事件的元方法即可以是函数,也可以是一张表。如果是一个函数,则以 table、 key、以及 value 为参数传入。如果是一张表, Lua 对这张表做索引赋值操作

__call: 函数调用操作 func(args)。 当 Lua 尝试调用一个非函数的值的时候会触发这个事件(即 func 不是一个函数)。查找 func 的元方法__call,如果找得到,就调用这个元方法, func 作为第一个参数传入,原来调用的参数(args)后依次排在后面。

__gc: 当一个被标记的对象成为了垃圾后,垃圾收集器并不会立刻回收它。取而代之的是,Lua 会将其置入一个链表。在收集完成后,Lua 将遍历这个链表。 Lua 会检查每个链表中的对象的 __gc 元方法:如果是一个函数,那么就以对象为唯一参数调用它;否则直接忽略它。简而言之,就是当GCOject被回收时,触发__gc元方法执行。

举个小例子:

 1 do
 2 local ta = {c=2}
 3 local me = {}
 4
 5 me.__call = function(tab, arg)
 6     print("__call", tab, arg)
 7     return type(tab), type(arg),1
 8 end
 9
10 me.__newindex = function(tab, key, value)
11     print("__newindex", tab,key,value)
12     rawset(tab, key, value);
13 end
14
15 me.__index = function(tab, key, value)
16     if key == "n" then
17         return function(value) return value * 10 end
18     end
19
20     print("__index", tab, key, value)
21     return 10
22 end
23
24 me.__gc = function(tab)
25     print("__gc", tab)
26 end
27
28 setmetatable(ta, me);
29
30 local t = ta();
31 print(ta())
32 ta.a = 1
33 ta.c = 2
34
35 local c = ta.m
36 print("c=" .. c)
37
38 local d = ta.n(12)
39 print("d=" .. d)
40
41 end
42
43 collectgarbage("collect");

运行结果如下:

__call  table: 0x1e2e180        nil  //local t = ta()
__call  table: 0x1e2e180        nil  //print(ta())
table   nil     1                  //print(ta())
__newindex      table: 0x1e2e180        a       1  //ta.a = 1
__index table: 0x1e2e180        m       nil         // local c = ta.m
c=10
d=120
__gc    table: 0x1e2e180

4、对象的存储位置和生存周期

  lua userdata和lightuserdata是用来存储C++对象的两种主要方式。

  • lightuserdata类型对应为LUA_TLIGHTUSERDATA,实际上就是一个指针void*,需要在C/C++层面创建对象,把对象指针存放为lightuserdata类型,因为这不是一个GC对象,需要由C/C++层面创建和释放,比较适合应用在一些需要在C/C++层面创建一些全局对象的场合。
  • userdata类型对应为LUA_TUSERDATA,len+data,属于lua层的GC对象,会通过lua的gc机制进行回收。如果userdata定义了原表的__gc方法,在回收前会调用__gc方法。

  在创建C++层的对象时,是在C++层管理对象的生命周期还是在lua层通过gc来自动回收,完全取决于的用户想怎么控制。

原文地址:https://www.cnblogs.com/liao0001/p/9791087.html

时间: 2024-10-07 21:21:14

lua绑定C++对象系列一——基础知识的相关文章

.net中的并行编程系列-1.基础知识

最近半年一直研究用.net进行并行程序的开发与设计,再研究的过程中颇有收获,所以画了一个图总结了一下并行编程的基础知识点,这些知识点是并行编程的基础,有助于我们编程高性能的程序,里面的某些结构实现机制也蕴含着丰富的软件设计思想,在后续的文章中我会对图里面提到某些数据结构或同步机制的源码进行分析. 注:虽然使用的平台是.net ,但大部分知识点和平台以及语言无关,相关数据结构其他相关平台都有实现,包括优化手段都非常相似. .net中的并行编程系列-1.基础知识,布布扣,bubuko.com

[Solution] 单元测试系列(1) 基础知识

单元测试不是软件开发的新概念,在1970年就一直存在,屡屡被证明是最理想的方法之一. 本系列将分成3节: 单元测试基础知识 打破依赖,使用模拟对象,桩对象,模拟框架 创建优秀的单元测试 本节索引: 单元测试与集成测试 测试驱动开发 UnitTest和NUnit 第一个单元测试 命名规范 单元测试与集成测试 单元测试几乎总是基于框架来写的,因为框架可以为我们提供统一的API来管理测试. 常用的框架有Unit Test(MS Test),NUnit(开源) 定义 单元测试是一段代码调用另一段代码,随

指针学习系列一 基础知识

本系列文记录我对指针的理解,从新手入门级写起,文字代码全手写,大神们发现错误请给我留言,谢谢!欢迎进一步讨论可以联系邮件[email protected] -1- 基础知识 → 1指针是一种变量类型,int*和int一样,指针指向变量a是指指针的内容是变量a的地址. 例1 对比普通数据类型 int a=1; //内存腾出一块4k的空间,首地址给a(设120),a=1则将1填进这块内存 int* b=&a; //内存腾出一块空间,依据指针所占内存和机子有关,首地址给b(设250),b=&a则

管理聚合链路和桥接网络(多网卡绑定,redhat liunx 7.0) 基础知识

管理聚合链路和桥接网络(多网卡绑定) 一 配置bonding接口,实现多网卡绑定 绑定多网卡,可以有以下优点: 提升网络传输能力 避免网络链路单点故障 bonding的两种绑定工作模式:实际上有7种,其他不常用 0  balance-rr  轮训模式 1  active-backup  高可用模式 逻辑原理 --eth0--- app  --数据发送到---bond0               ---switch---- --eth1--- 创建步骤: 创建主接口(master) bondx

k8s系列0--Kubernetes基础知识

Kubernetes介绍 参考:Kubernetes核心组件解析 Pod是k8s的最小调度单元 每个pod有独立的IP,但是pod的IP是不可靠的,重新调度pod就会改变IP,service概念就是为了解决这些问题. 给service会分配虚拟IP即ClusterIP,通过ClusterIP和端口,访问会自动负载均衡到后端的pod上. 目前k8s提供两种负载均衡策略:RoundRobin和SessionAffinity. RoundRobin:轮询模式 SessionAffinity:基于客户端

一、redis系列之基础知识与centos下环境搭建

1. Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用. Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储. Redis支持数据的备份,即master-slave模式的数据备份. 2. Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行. 3.Redis支持五种数据类型:string(

C++入门课程系列:基础知识篇(1)

C++是一种静态数据类型语言. ? 在C++语言中,变量是计算机编程的一个重要概念,它是一个存储值的字母或名称? 有几种基本类型的变量:string ("一组词").chars('a').floats(10.2).booleans(true或false 值)这几个是最常见的 创一个小群,供大家学习交流聊天如果有对学C++方面有什么疑惑问题的,或者有什么想说的想聊的大家可以一起交流学习一起进步呀.也希望大家对学C++能够持之以恒C++爱好群,如果你想要学好C++最好加入一个组织,这样大家

javascript中BOM部分基础知识总结

一.什么是BOM BOM(Browser Object Document)即浏览器对象模型. BOM提供了独立于内容 而与浏览器窗口进行交互的对象: 由于BOM主要用于管理窗口与窗口之间的通讯,因此其核心对象是window: BOM由一系列相关的对象构成,并且每个对象都提供了很多方法与属性: BOM缺乏标准,JavaScript语法的标准化组织是ECMA,DOM的标准化组织是W3C,BOM最初是Netscape浏览器标准的一部分. 二.学习BOM学什么 我们将学到与浏览器窗口交互的一些对象,例如

php高级进阶系列文章--第五篇(redis基础知识复习)

在实际工作中不仅需要使用到非常重要的关系型数据库mysql,同时也需要使用到redis,memcached,mongodb等进行数据存储,完成特定的产品需求,本节先复习redis的基础知识,高深部分后续会继续探讨 1,redis与其他的key-value形式缓存数据库的特点: 支持数据持久化,将内存中的数据保存到磁盘中,重启的时候再次加载进行使用 redis不仅有key-value格式的数据,同时也有list,set,zset,hash格式的数据结构存储数据 redis支持数据的备份,即Mast