tolua++初始化过程

当在c++的工程使用lua时,可以通过tolua++将c++的类注册到lua中,从而使得可以在lua中使用c++中的类和对象。先不说其主要可以做些什么,当了解到更多时再进行解说。现在这里仅仅说明toua++初始化时的动作。

假设当前有两个类:CMyObject和CStudent,CStudent继承CMyObject,如下:

CMyObject:

class CMyObject
{
public:
    CMyObject();
    ~CMyObject();
};

CStudent:

class CStudent:public CMyObject
{
public:
    CStudent(void);
    ~CStudent(void);

    void Run();
    void Run(int a);
};

编写的pkg文件如下:

Student.pkg:

$#include "MyObject.h"
$#include "Student.h"

class CMyObject
{
public:
    CMyObject();
    ~CMyObject();
}

class CStudent:public CMyObject
{
public:
    CStudent();
    ~CStudent();

    void Run();
    void Run @ Run2(int a);
};

使用tolua++.exe通过命令生成lua_CStudent.cpp文件,其中就是通过tolua++导入lua中的函数。其中初始化函数为tolua_Student_open(lua_State* tolua_S),一般会在main函数中,在luaL_newstate创建了一个新的lua状态指针后调用,来将c++中的类注册到lua环境中接着就可以直接执行lua文件。现将其中的主要步骤说明如下:

特殊符号的说明:table1-->table2表示的为table2为table1的元表;

1、tolua_open:其中,首先判断当前的环境是否已经被打开,如果未打开,则会在lua注册表(lua中,注册表是一个特殊的table,可以通过伪索引LUA_REGISTRYINDEX来进行操作)中写入一个键值对reg["tolua_opened"=true]。接下来是对一些公用的table进行初始化,包括,但不限于reg["tolua_ubox"=table-->table["__mode"="v"]]、reg["tolua_super"=table]、reg["tolua_gc"=table]、reg["tolua_commonclass"=table]等,接下来会在全局表_G(后面使用global代替)中,创建一个key为"tolua"的table,并在其中写入C++函数,如下:global["tolua"]["type"=tolua_bnd_type]、global["tolua"]["takeownership"=tolua_bnd_takeownership]、global["tolua"]["releaseownership"=tolua_bnd_releaseownership]、global["tolua"]["cast"=tolua_bnd_cast]、global["tolua"][“inherit”=tolua_bnd_inherit]、global["tolua"]["setpeer“=tolua_bnd_setpeer]、global["tolua"]["getpeer"=tolua_bnd_getpeer]。这些函数,暂时跟踪代码并没有发现有用到的地方。

2、tolua_reg_types:从命令可以看出来是注册类型,其中就是将CMyObject和CStudent注册到lua环境中。这里的注册比较简单,就是在lua注册表中创建两个键值对,其中键就是两个类名称,而值为两个table,即reg["CMyObejct"=table]和reg["CStudent"=table]。其中使用的是lua提供的函数来进行注册:luaL_newmetatable,这个是辅助库中的函数。lua5.1的参考文档中说,必须用此函数来创建的table才可以作为其他table的元表。其实,也就是在lua注册表中创建了table,然后将这个table又插入到当前的栈中。创建了两个table后,又在这两个table中写入了写元表的默认操作,拿reg["CStudent"]来说,即有如下操作:

reg[“CStudent”][“__index”=class_index_event,  “__newindex”=class_newindex_event,    “__add”=class_add_event,   “__sub”=”class_sub_event”,   “__mul”=class_mul_event,   “__div”=class_div_event,  “__lt”=class_lt_event,  “__le”=class_le_event,    “__call”=class_call_event],从这里可以看出来,这个表在后面肯定会作为其他对象的元表,更大胆的猜测就是作为创建的CStudent的c++对象的元表,因为把c++对象注册到lua环境中时,使用的是userdata类型,其在lua环境中没有任何的默认操作。

接着设置操作了一次tolua_super对应的table,是设置当前类的基类。例如CStudent的基类为CMyObject,则在reg["tolua_super"]中有reg["tolua_super"][reg["CStudent"]=table["CMyObject"=true]]。假如CStudent再有一个基类CTest的话,则为reg["tolua_super"][reg["CStudent"]=table["CMyObject"=true, "CTest"=true]],这样可以查询到当前类的基类。

3、tolua_cclass(tolua_S,"CStudent","CStudent","CMyObject",tolua_collect_CStudent):这个函数中主要动作有两个 1)将基类作为继承类的元表,接着在reg["CStudent"]中生成一个新的tolua_ubox的table,即reg["CStudent"]["tolua_ubox"=table],这个table会在后面用到。 2)设置继承同2中的reg["tolua_super"]的操作。所以从这里可以看到有些重复,但是具体为何这么做,是因为代码是自动生成的,所以不会那么严谨还是其他原因?暂时没有去深究。

4、将c++中类的成员函数注册到lua中。例如注册CStudent中的Run函数,这是在reg["CStudent"]中插入一对键值对,键为函数名称,值为tolua++为CStudent的Run函数生成的一个外壳函数,名称为“tolua_Student_CStudent_Run00”(这个函数在使用tolua++.exe命令生成C++外壳时,tolua++给Run函数生成的外壳函数),执行操作reg["CStudent"]["Run"=tolua_Student_CStudent_Run00],其他需要暴露给lua的函数也都执行类似操作即可。此时,所有需要注册到lua中的函数已经全部完成。

5、在lua中创建CStudent的对象,由于tolua++会自动为CStudent生成一个new的外壳函数,用来创建c++的对象。在中间会使用new创建一个CStudent的对象,然后将此对象作为userdata压入到lua栈中,接着将reg["CStudent"]作为userdata的元表。有两点需要理解:1)tulua_ubox:在使用tolua++的tolua_pushusertype中,首先会查找当前类在lua中对应的table,即上面注册进lua的reg["CStudent"]中的"tolua_ubox"对应的table,即reg["CStudent"]["tolua_ubox"] ,这里在第3步中有说到。假如这个table不存在,会去查找reg["tolua_ubox"]。然后再reg["CStudent"]["tolua_ubox"]中查找当前的userdata是否已经在其中,如果在的话,就不进行新的注册;否在,将新的userdata压入其中。在这里压入userdata时使用了一个lightuserdata和userdata的键值对。在压入reg["CStudent"]["tolua_ubox"]的同时,在栈空间同样留了一个索引。然后再讲reg["CStudent"]设置为新的userdata的元表。 2)如何将userdata关联到相应的函数的:这里tolua++做了一个处理,在将类CStudent注册进lua时,在reg["CStudent"]中压入了一个函数reg["CStudent"]["__index"=class_index_event](在2中有提到)。这里简单说明下元表中的__index域,这是一个特殊的域,在lua查找一个对象的域的时候,当查找不到时,会去查看它的元表,当有元表时,就会调用元表中的__index域。tolua++中正是用了这个原理,当在lua环境中查找CSudent对象的函数,由于在lua环境中,C++对象是一个userdata,因此,会去查看其元表。而它的元表为reg["CStudent"],其中有__index域。__index域在tolua++中对应的为一个函数:class_index_event,为tolua++自定义的函数。在这个函数中会根据传入的参数在reg["CStudent"]中查找对应的函数。例如调用CStudent中Run成员函数,则在class_index_event会出入两个参数,一个位userdata,另外一个则为Run字符串。这样就可以在userdata的元表reg["CStudent"]中根据字符串Run查找到对应的函数,然后进行调用。

暂时理解的tolua++的过程即为此,后面当理解深的时候,还会对本文进行修改。如有不对的地方,欢迎网友指出,谢谢。

时间: 2024-11-10 12:58:59

tolua++初始化过程的相关文章

java代码的初始化过程研究

刚刚在ITeye上看到一篇关于java代码初始化的文章,看到代码我试着推理了下结果,虽然是大学时代学的知识了,没想到还能做对.(看来自己大学时掌握的基础还算不错,(*^__^*) 嘻嘻--)但是博主写的不够详细具体,我想在这详细谈一下java代码的具体初始化过程. 首先要清楚,初始化分为两个过程:类初始化.对象初始化. 类初始化是指类加载器将类加载到内存时,对类成员的初始化过程,也就是有static修饰的变量.对于加载完的类,它的类变量都会赋一个默认值,即使你定义时就赋值了.比如int类型就是0

JAVA对象的初始化过程

出处:http://blog.csdn.net/andrew323/article/details/4665379 下面我们通过两个例题来说明对象的实例化过程. 例1:   编译并运行该程序会有以下输出 Static Block Employee Company:china soft Non-Static Block Employee phone:0755-51595599 Employee(String) Empoloyee() 下面我们来对结果做分析: 1 在代码34行实例化对象时, 先对给

Java对象相关元素的初始化过程

1.类的成员变量.构造函数.成员方法的初始化过程 当一个类使用new关键字来创建新的对象的时候,比如Person per = new Person();JVM根据Person()寻找匹配的类,然后找到这个类相匹配的构造方法,这里是无参构造,如果程序中没有给出任何构造方法,则JVM默认会给出一个无参构造.当创建一个对象的时候一定对调用该类的构造方法,构造方法就是为了对对象的数据进行初始化.JVM会对给这个对象分配内存空间,也就是对类的成员变量进行分配内存空间,如果类中在定义成员变量就赋值的话,就按

对Socket CAN的理解(5)——【Socket CAN控制器的初始化过程】

转载请注明出处:http://blog.csdn.net/Righthek 谢谢! 对于一般的CAN模块,进行初始化时,最关键的是以下两步: 1.  配置CAN的位时序: 2.  配置CAN的消息报文: 下面,我们来详细分析上面提到的关键两步. 一.初始化步骤: 1.  第一步,进入初始化模式,在CAN控制寄存器中,将Init位置1: 2.  第二步,在CAN控制寄存器中,将CCE位置1: 3.  第三步,等待Init位置1,此步聚为了确保已经进入初始化模式: 4.  第四步,将位时序的值写入到

IOC容器的初始化过程

1.ClassPathXmlApplicationContext类体系结构 左边的黄色部分是ApplicationContext体系继承结构,右边是BeanFactory结构体系,两个体系是典型的模板方法设计模式的使用. 从该继承体系可以看出: (1)BeanFactory是一个bean工厂的最基本定义,里面包含了一个bean工厂的几个最基本方法:getBean(),containsBean()等,是一个很纯粹的bean工厂,不关注资源.资源位置.事件等. ApplicationContext是

Java初始化过程

以下程序执行的结果是: class X{ Y y=new Y(); public X(){ System.out.print("X"); } } class Y{ public Y(){ System.out.print("Y"); } } public class Z extends X{ Y y=new Y(); public Z(){ System.out.print("Z"); } public static void main(Stri

启动期间的内存管理之初始化过程概述----Linux内存管理(九)

日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.7 X86 & arm gatieme LinuxDeviceDrivers Linux内存管理 在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后内核才能检测到可用内存和寄存器. 而我们今天要讲的boot阶段就是系统初始化阶段使用的内存分配器. 1 前景回顾 1.1

继承的初始化过程

看think in java 7.9.1继承的初始化过程的例子,并没有讲到static 代码块的初始化顺序 类在初次使用的时候才会被加载,static代码会在加载的时候初始化,所以一个类按照先加载static变量—>static 代码块->普通变量->构造器代码 的顺序进行初始化 而出现继承的时候,类的初始化顺序如下 1.父类的static 变量 2.父类的static 代码块 3.子类的static 变量 4.子类的static 代码块 5.父类的变量 6 父类的构造器 7 子类的变量

Java父类子类的对象初始化过程

摘要 Java基本的对象初始化过程,子类的初始化,以及涉及到父类和子类的转化时可能引起混乱的情况. 1. 基本初始化过程: 对于一个简单类的初始化过程是: static 修饰的模块(static变量和static 块)  ---> 按照代码顺序依次执行. | 实例变量  及非static模块---> 按照代码顺序依次执行. | 构造函数 ---> 执行对应的构造函数. 子类的初始化过程. 父类static修饰的模块 | 子类static修饰模块 | 父类实例变量和非static块 | 父