Basic types
Tolua自动将C/C++的基本类型映射到lua的基本类型。因此,char, int, float, 和 double映射到Lua的number类型;char *映射到string;void *映射到userdata。Types may be preceded by modifiers (unsigned, static, short, const, etc.);然而,认识到tolua忽略了用于修饰基本类型的const(be aware that tolua ignores the modifier const if applied to basic types)。因此,如果我们传递一个基本类型的常数(conston)给Lua而后Lua再把该数返回给C/C++,这时非常量到常量之间的转换会在后台悄悄的进行。
C/C++的函数也可以明确的使用Lua的对象。因此lua_Object也被认为是一个基本类型。所以,任何的Lua都满足这一要求。
新的tolua + + :在C++中的string类型同样被认为是基本类型,会被当作值创递给lua(使用c_str()方法)。这个功能可以使用命令行-S进行关闭。
User defined types
所有在package文件里的其他类型都会被认为是用户自定义类型。它们会映射到Lua的userdata类型。Lua只能存储指向用户定义类型的指针;但是,tolua会自动产生必要的安排来处理引用和值。例如,如果一个函数或方法返回一个用户定义类型的值,当这个值返回lua的时候,tolua会分配一个克隆对象,同时会设置垃圾收集标记,用于在Lua不再使用该对象时自动释放。
对于用户定义类型,常量是被保留的。因此将非常量用户定义类型数据作为常量参数传递给一个函数,会产生类型不匹配的错误。
NULL and nil
C/C++ 的NULL或0指针映射到Lua的nil类型;反之,nil可能会指向任何可能的C/C++指针(conversely, nil may be specified wherever a C/C++ pointer is expected.)。
Typedefs
Tolua还接受package文件简单的typedef(tolua also accepts simple typedef's inside the package files.)。任何一种类型当定义的时候,tolua会将其映射到基本类型。它们是很有用的因为一些package会将C/C++类型重新定制转为它们的类型。例如,一个package可以定义类型real来表示一个double类型。在这种情况下,real可用于被lua解析的指定类型变量的package文件(real can be used to specify the variable types inside the package file interpreted by tolua),但只有我们在使用之前包括下面的定义:
typedef double real;
否则,real将会被解释为用户定义类型而不会映射到Lua的number类型。
Including real header files
在package文件中,我们必须指定对应的头文件,这些头文件包含着我们要访问的常数,变量,函数和类。在package文件里任意以$ (except $[hclp]file, $[ , and $] lines)开头的一行会被无修过的插入到生成的绑定到C/C++的代码,但是会消除$号本身。我们用这个特性类引入真正的头文件。所以,我们的头文件一般会设置$作为开始,指定对应要引用的文件,那些package需要的文件。
/* specify the files to be included */
$#include "header1.h" // include first header
$#include "header2.h" // include second header
As illustrated, tolua also accepts comments, using C or C++ convention, inside the package file. Nested C-like comments can also be used.
还注意到,文件中的$cfile或$hfile不需要使用该方式,这个tolua自动完成。
在下面的章节,我们将介绍如何指定我们想要绑定到lua的C/C++代码。这些格式简单并且符合C/C++的声明方式。
Binding constants
Tolua支持两种用于绑定常数的方式:define's和enum's。对于define's的一般格式是:
#define NAME [ VALUE ]
上面的VALUE是可以选择的(The value, as showed above, is optional)。如果这样的代码出现在要被解析的文件中,tolua会将NAME作为Lua的全局变量,该全局变量是C/C++的常量,值为VALUE。只接受数字常量。
新的tolua++:所有其他预处理指令会被忽略。
对于enum's的一般格式是:
enum {
NAME1 [ = VALUE1 ] ,
NAME2 [ = VALUE2 ] ,
...
NAMEn [ = VALUEn ]
};
同样的,tolua创建一系列全局变量,命名为NAMEi,对应values。
Binding external variables
全局外部变量同样可以暴露出来。在已清理的头文件(cleaned header file),他们都指定为:
[extern] type var;
Tolua将这种声明绑定到Lua的全局变量中。因此,在Lua里,我们可以很自然的访问C/C++变量。如果变量不是常数,我们还可以在Lua赋予该变量新的值。全局数组同样可以被绑定到Lua里。数组可以是任意类型。数组对应的Lua类型是table,对应以数字作为下标(indexed with numeric values);但是,要注意的是Lua中下标1对应C/C++中的下标0。数组一定要定长(be pre dimensioned)。例如
double v[10];
新的tolua++:外部变量可以使用tolua_readonly修饰。(see Additional Features)
Binding functions
函数也指定为传统的C/C++声明:
type funcname (type1 par1[, type2 par2[,...typeN parN]]);
返回类型可以是void,表示没有返回值。函数也可以没有参数。在这种情况下,void可能被指定到函数的参数列表里(specified in the place of the list of parameters.)。参数类型必须遵循发布(posted)的规则。Tolua创建一个Lua函数绑定C/C++函数。当Lua调用函数时,参数类型必须对应C/C++的类型,否则tolua会产生一个错误并报告错误参数。如果参数名是省略的,tolua会自定命名,但是类型必须是基本类型或者是以前使用过的用户定义类型。
Arrays
Tolua同时支持以数组作为参数的函数或方法。最每秒的事情是,当数组映射到lua的table后,当C/C++函数改变了数组里面的值,会实时更新到lua的table中。
数组的大小必须是事先定义好多。例如:
void func (double a[3]);
在tolua里这是一个合法的函数声明,在lua里调用这个函数方法如:
p = {1.0,1.5,8.6}
func (p)
数组长度不一定是一个常数表达式;还可以是在运行过程中能计算出结果的表达式。例如:
void func (int n, int m, double image[n*m]);
这个是合法的。因为表达式n*m在函数的绑定范围(expression n*m is valid in the binding function scope)。但是必须考虑到,tolua使用动态分配的方式绑定函数,这一方式会降低性能。
尽管尺寸匹配是必须的,但是更重要的是意识到,所有数组传递给实际C/C++函数是,是使用局部变量的方式进行传递。因此,如果C/C++函数想要保留这个数组供以后使用,这段代码可能不会如想象那样工作。
Overloaded functions
Tolua还支持重载函数。区分两个同名函数的方法是基于映射到lua的参数类型。因此
void func (int a);
void func (double a);
在C/C++中这代表两个不同的函数,然而在tolua里它们是同一个函数,因为int和double类型都映射到number。
另一个棘手的情况时,当遇到指针时。假设:
void func (char* s);
void func (void* p);
void func (Object1* ptr);
void func (Object2* prt);
尽管在C++中这是4个不同的函数,但是映射到Lua里面的声明:
func(nil)
重要的是,运行的时候tolua决定那个函数被调用,尝试映射到那些提供的函数。Tolua第一次尝试调用最后一个指定的函数;如果失败,tolua接着尝试前一个函数。这个过程一直持续到找到一个能成功运行的代码或者到第一个函数。因为这个原因,不匹配的错误信息的发出,总是基于第一个函数的规格(the mismatching error message, when it occurs, is based on the first function specification)。当性能很重要时,我们应该把最常用的函数放在最后一个。
Tolua支持C的重载函数。(see Renaming for details.)
Default parameter values
最后的函数参数可以带有默认值。因此,如果函数调用时提供的参数不足,默认值会自动使用。该格式指定的默认值与C++里面的是一致的。
type funcname (..., typeN-1 parN-1 [= valueN-1], typeN parN [= valueN]);
toLua执行此功能无需使用任何C++的机制;因此,同样支持绑定C的函数。
我们同样可以指定数组的默认值(没有办法指定一个默认值为数组本身)。例如:
void func (int a[5]=0);
把数组的所有值设为0.因此,这个函数可以直接在lua调用无需初始化table。
对于lua对象类型(lua_Object),tolua定义了一个常量用来指定nil为默认值:
void func (lua_Object lo = TOLUA_NIL);
新的tolua++ :C++类的构造是有效的默认参数。例如:
void set_color(const Color& color = Color(0,0,0));
Multiple returned values
在lua中函数可以返回任意个数的值。Tolua使用这个特性来模拟值传递。如果一个函数的参数指定为一个指针或引用的一个基本类型或一个指针或引用的一个指针一个用户定义的类型,tolua接受对应类型的传入和返回,除了常规的函数返回,还可以通过参数更新的形式。
例如,考虑用于交换数值的C函数:
void swap (double* x, double* y);
or
void swap (double& x, double& y);
如果这函数在package文件中声明,tolue绑定该函数为输入两个参数,返回两个值。所以,正确的Lua代码为:
x,y = swap(x,y)
如果输入值不使用,lua会自动使用默认参数值调用函数而不需要指定它们。
void getBox (double* xmin=0, double* xmax=0, double* ymin=0, double* ymax=0);
In Lua:
xmin, xmax, ymin, ymax = getBox()
如果是用户定义类型,则以下面为例:
void update (Point** p);
or
void update (Point*& p);
Binding struct fields
用户定义类型可以很好的被tolua绑定。对于每一个变量或函数类型,不符合基本类型, tolua自动创建一个标签的用户数据代表的C/C++类型。
如果类型对应结构,Lua可以通过下标直接访问结构里面的内容,indexing a variable that holds an object of such a type. 在C代码,这些类型通常是用typedef定义的:
typedef struct [name] {
type1 fieldname1;
type2 fieldname2;
...
typeN fieldnameN;
} typename;
如果这段代码正在被插入到package文件中,tolua允许任何拥有类型对象名的对象反问任何索引列出的变量的字段名字(If such a code is inserted in the package file being processed, tolua allows any variable that holds an object of type typename to access any listed field indexing the variable by the field name.)。例如,如果var持有对象, var.fieldnamei可以访问fieldnamei里面的东西。
块内的数组同样被映射。
typedef struct {
int x[10];
int y[10];
} Example;
Binding classes and methods
Tolua支持C++的类定义。事实上,tolua能很自然的处理单一继承和多态性。以下各小节将会介绍什么可以暴露给lua,在类定义的时候(The subsections below describe what can be exported by a class definition.)。