tolua++ 参考手册node
--刘源霖 (译)程序员
本文是对tolua++官网使用手册的翻译,若是读者在阅读的过程当中什么疑问可参考官方使用手册。在本文中不会添加任何译者的理解,彻底是安装文章笔者的原文进行翻译。数组
tolua++ 是tolua的一个扩展版本,是一个将C/C++代码集成到lua中的工具。Tolua++增长了面向C++的新功能和bug修正,好比支持std::string做为基础类型(能够经过命令行参数开关),支持模板类。框架
Tolua++工具集成C/C++代码到lua很是的简单方便,tolua可以基于一个简单的头文件(能够从多个头文件中提取)为lua访问C/C++功能自动生成绑定代码。经过使用lua API 和一些标记方法,tolua能为lua提供C/C++中的常数,外部变量,函数,类,方法。ide
该使用手册是针对tolua++1.0版本,实现是基于lua5.0和tolua5.0。若是想了解相对旧版本的兼容性细节能够访问tolua++官网。下面部分我将描述如何使tolua,若是发现任何错误或者建议能够联系tolua++官方团队。函数
使用tolua,咱们须要建立一个pkg文件,即一个简单的C/C++头文件,该文件包括咱们想要绑定到lua中的常数,外部变量,函数,类,方法。而后使用tolua解析pkg文件,生成一个自动绑定C/C++代码到到lua的C/C++文件,只要咱们将生成的文件连接到咱们的应用程序中,就能在lua中访问咱们在pkg文件中指定的C/C++代码,即咱们定义的常数,外部变量,函数,类,方法。一个pkg文件可以包含正规的C/C++头文件、其余pkg文件以及lua文件。工具
让咱们一个例子开始,假定咱们指定绑定下面相似C 头文件的pkg文件到lua中:性能
a.pkg测试
int foo(lua_State *L)this
#define FALSE 0
#define TRUE 1
enum {
POINT = 100,
LINE,
POLYGON
}
Object* createObejct (int type);
void drawObject (Object* obj, double red, double green, double blue);
int isSelected (Object* obj);
使用tolua++解析a.pkg生成一个自动绑定上面代码的C文件,连接到咱们的应用中,而后咱们能够在lua中这样使用绑定的C 代码, 用一个实例来表示:
a.lua
...
myLine = createObject(LINE)
...
if isSelected(myLine) == TRUE then
drawObject(myLine, 1.0, 0.0, 0.0);
else
drawObject(myLine, 1.0, 1.0, 1.0);
end
...
一样,导入一个类C++的头文件:
b.pkg
#define FALSE 0
#define TRUE 1
class Shape
{
void draw (void);
void draw (double red, double green, double blue);
int isSelected (void);
};
class Line : public Shape
{
Line (double x1, double y1, double x2, double y2);
~Line (void);
};
用tolua解析这个文件,将会自动生成一个C++文件,该文件绑定上面的code到lua中,导入的代码能够下面这样有效的使用:
...
myLine = Line:new (0,0,1,1)
...
if myLine:isSelected() == TRUE then
myLine:draw(1.0,0.0,0.0)
else
myLine:draw()
end
...
myLine:delete()
...
提供给tolua解析的这个pkg文件(一般用扩展名.pkg表示)不是一个真正的C/C++头文件,可是是真正C/C++头文件的一个更简洁的版本。Tolua没有实现彻底的解析C/C++代码,可是它能解析一些的声明,这些声明是用来描述咱们要导入到lua的功能。正规的头文件能够包含在pkg文件中,tolua将从头文件中提取用户指定的代码进行解析。
Tolua 由两部分组成:一个可执行文件和一个库文件。这个可执行文件就是一个解析器,它读取pkg文件进行解析,输出一个对应的C/C++文件,这个C/C++实现了自动绑定pkg文件中指明的code到lua中,让lua中可以正常的访问。若是这个pkg文件是一个相似于C++的头文件(即包含类的定义),那么将自动生成一个C++文件,若是这个pkg文件是一个相似于C的头文件,那么将自动生成一个C文件,tolua命令支持带参数运行,运行“tolua –h” 能够查看当前tolua命令支持的参数集。例如,利用tolua解析一个名为myfile.pkg的文件生成其绑定代码到myfile.c文件中,咱们能够用命令:
tolua –o myfile.c myfile.pkg
这生成的文件必须编译,而且连接到咱们的应用程序中,这样才能为lua提供访问,每个解析的文件被看成一个包导入到lua中。默认状况下,这个包名就是导入文件的根文件名(在上一个例子中包名就是myfile)。用户还能够指定不一样报名:
tolua –n pkgname –o myfile.c myfile.pkg
这个包必须被明确的初始化。为了在C/C++代码中初始化这个包,咱们必须声明和调用这个初始化函数,这个初始化函数被定义为:
Int tolua_pkgname_open(lua_State *);
其中pkgname就是咱们要初始化的包名。若是咱们使用的是C++,咱们还能够选择自动初始化:
Tolua –a –n pkgname –o myfile.c myfile.pkg
这中状况下,这个初始化函数将会自动被调用,可是若是咱们计划使用多个lua states,则不能使用自动初始化,由于那些在C++中被初始化的静态变量的顺序没有被定义。
这个open函数的原型也能够选择输出到一个头文件中,这个头文件名字能够用—H 选项指定。
tolua自动生成的绑定代码使用了tolua库中的函数集,因此这个库必须被连接到咱们的应用程序中。自动生成的绑定代码中必须包含头文件tolua.h。
一个应用程序能够不绑定任何包,直接使用tolua提供的面向对象框架(具体见通用函数导出章节),可是必须调用tolua的初始化函数(这个函数在任何的包初始化函数中被调用):
Int tolua_open(void);
使用tolua的第一步是建立一个pkg文件。以一个真实的头文件开始,我须要按照tolua可以解析的格式去清理咱们项目想要导入到lua中的功能。这tolua可以解析的格式是如下方式描述的一个简单的C/C++声明。
一个pkg文件可能包含其余pkg文件,其格式以下:
$pfile “include_file”
一个pkg文件可能包含正规的C/C++头文件,用hfile 或者cfile指令:
$cfile “example.h”
在这种状况下,tolua将会提取在“tolua begin” 和 “tolua_end”之间的代码,或者用“tolua_export”指定的单行代码。如下面的C++头文件为例:
#ifndef EXAMPLE_H
#define EXAMPLE_H
Calss Example { //tolua_export
private:
string name;
int number;
public:
void set_number(int number);
//tolua_begin
string get_name();
int get_number();
};
//tolua_end
#endif
在这个例子中,不被tolua支持的代码(类的私有变量)和函数set_number 将会被舍弃,不会包含到pkg文件中。
最后,lua文件也能够包含在pkg文件中,使用“$lfile”:
$lfile “example.lua”
tolua++ 扩展:
从版本1.0.4开始新增长了一个包含资源文件的扩展方法,使用命令:$ifile:
$ifile "filename"
Ifile 一般在文件名的后面使用附件参数, 例如:
$ifile "widget.h", GUI
$ifile "vector.h", math, 3d
Ifile的默认行为是包含文件的缘由内容,可是,这包含文件的内容和扩展参数在包含进pkg文件前先要调用include_file_hook函数(细节前参考定制tolua++章节)。
Tolua 自动映射C/C++j接班类型到LUA的基本类型,所以:char, int , float,double被映射到Lua的number类型;char * 被映射成string;void * 被映射成userdata;每一个类型类型可能被加上前缀修饰语(unsigned, static, short, const, etc.);然而,假如基本类型前加上const,tolua将会忽略它。所以,假如咱们传递一个常数基本类型到lua,而后从lua再传回给C/C++代码时将会变成一个很是数类型,这种常数类型到很是数类型的转换将被默认完成。
在C/C++函数中也能明确的使用lua对象,所以,“lua_Object”类被看做一个基本类型,任何的Lua 值均可以用lua_Object表示。
Tolua++ 扩展:
C++类型string 被看做基本类型,以及能够被做为一个值传递到lua中(使用c_str()方法)。这个功能能够经过命令行参数-S关闭。
全部在pkg文件中出现过的其余的类型被看做用户定义类型,他们被映射成lua中标记的userdata类型。Lua仅仅可以存储用户定义类型的指针。尽管如此,tolua++会自动生成一些必要的步骤来处理引用和值。举个例子,假如在函数或者方法中返回了一个用户定义类型,当返回它到lua时,tolua分配一个克隆的对象以及设置其垃圾回收标记方法,以便在lua再也不使用时自动回收分配对象。
对于用户定义类型,常量是被保存的。所以,传递一个很是量的用户定义类型到一个须要参数是常量类型的函数中时将会产生一个类型不匹配的错误(type mismatch error)
C/C++ NULL或者0指针被映射成lua中的nil类型,相反的,nil能够给任何一个C/C++指针。将char* ,void*以及指针转换成用户定义类型是有效的。
Tolua 也支持简单的类型定义(typedef),一个类型被从新定义后,其后任何出现的地方都会被tolua映射成基本类型。这是很是有用的,每一个包能够经过C/C++的基础类型从新定义本身的类型。例,某个包能够定义类型real 看成 double类型,这样,real就能够在pkg文件中定义变量的类型了。可是在咱们使用前必须包含以下的定义:
typedef double real;
不然,real将会被解释成一个用户定义类型,而不是lua的number类型。
在pkg文件中,咱们必须指定包含真正的头文件,以致于tolua生成的代码中可以正常的访问咱们要绑定到lua中的常量,变量,函数,类等。在pkg文件中任何以$符号开始的一行(除$[hclp]file, $[ , $] 开头行)将会不做任何修改的插入到生成的绑定代码中,可是要去除$符号。咱们使用这个功能来包含真正的头文件,因此,一般在pkg文件的开始位置咱们会用$符号包含咱们指定的头文件,这些头文件是咱们pkg文件的基础。
/* specify the files to be included */
$#include "header1.h" // include first header
$#include "header2.h" // include second header
从上面的代码能够看出,tolua也接受注释,注释使用C/C++习惯方式。
一样能够注意到使用$hfile 或者 $cfile 不须要使用include这种方式,这些被toulua自动完成了。
在接下的章节中,咱们将描述如何指定咱们想要绑定到lua中的C/C++代码, 这格式是一个简单的有效的C/C++声明。
为了绑定常量,tolua支持define和enum,对于define的支持其格式以下:
#define NAME [VALUE]
VALUE 是可选的,将上面的代码包含在pkg文件中,tolua生成的绑定代码容许lua中把NAME看成一个全局变量使用,而且其值和C/C++中的值同样。可是仅仅只支持数值常量。
Tolua++ 扩展:因此其余预处理将会被忽略。
对于enum支持格式以下:
enum {
NAME1 [ = VALUE1 ] ,
NAME2 [ = VALUE2 ] ,
...
NAMEn [ = VALUEn ]
};
一样的,tolua建立了多个全局变量,以及他们对应的值。
全局的外部变量能够被导出,在pkg文件中的他们的指定格式以下:
[extern] type var;
Tolua绑定此声明到Lua的全局变量中,所以在Lua中咱们可以天然的访问C/C++变量,假如这个变量是一个很是量,咱们在lua中为其赋新值。全局数组也能够绑定到乱中,数组能够是任何类型,与之对应的lua对象是table,table的以数字作索引,只是table中时从1开始,而数组中是从0开始。例:
double v[10];
tolua++扩展:外部变量咱们能够用tolua_readonly 修饰(见附件功能章节)
函数的指定按照日常的C/C++函数声明:
type funcname (type1 par1[, type2 par2[,...typeN parN]]);
这返回的类型能够是空,意味着没有值返回。函数也能够没有参数,参数链表用void表示。参数类型必须准信以前的说明的规则。Tolua建立一个Lua 函数 绑定这个C/C++函数,在lua中调用这个函数时,参数类型必须匹配,不然toua将会生成错误信息,而且报告那个参数有错误。假如参数的名字是省略的,tolua将会自动为期命名,可是其类型必须是基本类型或者以前已经定义的用户类型。
Tolua也可以处理函数或者方法参数是数组时,数组参数对应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]);
可是上面这种使用方法,tolua是使用动态分配法来绑定函数的,因此其性能要低一些。
尽管规定了数组的大小,可是咱们必须知道全部传入实际C/C++函数的数组变量在绑定函数都是局部变量,因此,若是被调用的C/C++函数若是要保存数组的指针在函数调用后使用,那么这绑定代码将会出现异常。
重载函数也是被支持的,可是必须记住,两个名字相同的函数的区别是基于其映射到lua中的参数类型。因此,尽管:
void func (int a);
void func (double a);
这两个函数在C++中是不一样的,可是他们对于lua来讲是相同的函数,由于int和double映射到lua中的函数都是number类型。
还有另一种特殊的状况,当全部的参数都是指针时,如:
void func (char* s);
void func (void* p);
void func (Object1* ptr);
void func (Object2* prt);
上面四个函数在C++中时不一样的,可是在lua中用一个函数均可以匹配他们:
func(nil)
因此知道tolua是如何解决在运行时调用那个函数时很是重要的,其方法就是就是尝试匹配没一个提供的函数,首先匹配最后一个被指定的函数,假如失败了,继续匹配前一个被指定的函数,重复这过程,直到有函数匹配或者匹配到第一个被指定函数。若是匹配到第一个被指定函数都尚未匹配成功,那么将会报告“mismatching error” 消息。但性能很重要时,咱们能够将最经常使用的函数放在最后一个指定,由于它将第一个被匹配。
Tolua 运行在C中使用重载,细节请看“重命名“章节。
函数的最后几个参数能够指定默认值,这样,在函数被调用时有默认值的参数能够不传入值,而是从默认值中获取。在pkg文件总指定默认值的格式和C++中同样。
type funcname (..., typeN-1 parN-1 [= valueN-1], typeN parN [= valueN]);
tolua 实现这个功能没有使用任何的C++机制,因此它也能够用于绑定C函数。
咱们也能够为数组的元素指定默认值(尽管没有方法为一个数组指定默认值),例:
void func (int a[5]=0);
上面代码指定数组元素的默认值是0,因此这个函数在lua中可使用一个没有被初始化的表来调用。
对于Lua object 类型(lua_object), tolua 定义了一个一个常理,它指定为nil做为一个默认值。
void func (lua_Object lo = TOLUA_NIL)
tolua++扩展:
C++构造器做为默认参数是有效的,例:
void set_color(const Color& color = Color(0,0,0));
在lua中,一个函数能够返回任意多个值,tolua使用这个功能经过引用模拟值传
递。假如一个函数的参数使用指针或者引用做为参数类型,tolua将支持对应的类型做为输入而且返回对应的类型。
例:
void swap (double* x, double* y);
or
void swap (double& x, double& y);
假如这个函数被声明在pkg文件中,tolua 将会以一个有两个输入参数,两个返回值的函数绑定它。因此在lua中正确的调用:
x,y = swap(x,y)
使用默认值的状况:
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);
Tolua完美的支持用户定义类型绑定,对于不是基础类型的变量类型或者函数类型,tolua会自动建立一个标记的userdata来看成这个类型。假如类型是一个结构体,这个结构体的域能够直接在lua中访问,在C代码中,类型定义通用typedef:
typedef struct [name] {
type1 fieldname1;
type2 fieldname2;
...
typeN fieldnameN;
} typename;
假如上面的代码被绑定到lua中,咱们能够这样访问,假如var是上面类型的一个变量或者对象,咱们能够经过var.fieldnamei 来访问类型中域名为fieldnamei的值。
通用支持域是数组的状况:
typedef struct {
int x[10];
int y[10];
} Example;
Tolua支持C++中类的定义,实际上,tolua使用了一个天然的方法处理单继承和多状态的。接下来的部分描述了类中那些可以被导入到lua中。
假如var是一个lua变量,var保存了一个派生类对象,var能够用于任何其基类的地方,而且var能够访问其基类的任何方法。为了这一机制可以生效,咱们必须指明派生类继承的基类。使用传统的方法,以下:
class classname : public basename
{
/* class definition */
};
如在lua中要使用继承的属性, 那么基类basename要在派生类classname前定义。
Tolua++从版本1.0.4开始支持多继承,容许你手动的访问其余的父类。
例:
class Slider : public Widget, public Range {
...
};
一个Slider的对象将会彻底的继承widget,以及会包含一个Range类型的成员,例:
For example:
slider = Slider:new()
slider:show() -- a Widget method
slider:set_range(0, 100) -- this won't work, because
-- set_range is a method from Range
slider.__Range__:set_range(0, 100) -- this is the correct way
这只是一个实验功能。
对于结构体域,类域,静态的或者非静态的均可以被导出。类的方法和类静态方法也能够别导出。固然他们在C++中必须是被定义为public的(这public: 关键之能够保留在pkg文件中,它将会被tolua忽略)。
每个绑定的类,tolua建立一个lua表来存储它,而且命令和C++类名同样。这个表当中可能包含其余表,表示这个类中可能包含其余类或者结构体。静态的方法经过表能够直接调用,非静态的必须经过对象访问,即一个包含对象的变量。
Tolua支持一些特殊的方法。构造器被定义为静态的,名字为new,new_local
(tolua++), 或者直接调用类名(具体的细节见下文),析构函数被定义为delete方法。
注意tolua支持重载,这个能够应用于构造函数,一样注意virtual关键字在pkgfile中无效。
下面是一个可以被tolua解析的pkgfile,例:
class Point {
static int n; // represents the total number of created Points
static int get_n(); // static method
double x; // represents the x coordinate
double y; // represents the y coordinate
static char* className (void); // returns the name of the class
Point (void); // constructor 1
Point (double px, double py); // constructor 2
~Point (void); // destructor
Point add (Point& other); // add points, returning another one
};
class ColorPoint : public Color {
int red; // red color component [0 - 255]
int green; // green color component [0 - 255]
int blue; // blue color component [0 - 255]
ColorPoint (double px, double py, int r, int g, int b);
};
导入lua后,咱们能够用如下的例子测试:
p1 = Point:new(0.0,1.0)
p2 = ColorPoint:new(1.5,2.2,0,0,255)
print(Point.n) -- would print 2
print(Point:get_n()) -- would also print 2
p3 = p1:add(p2)
local p4 = ColorPoint()
print(p3.x,p3.y) -- would print 1.5 and 3.2
print(p2.red,p2.green,p2.blue) -- would print 0, 0, and 255
p1:delete() -- call destructor
p2:delete() -- call destructor
注意:咱们仅仅可以delete咱们本身建立的对象,p3将会被自动回收,咱们不可以delete它。
Tolua++扩展:须要注意p4是直接调用类名建立的,这种方式和调用new_local方法功能是同样的,当不适用这个对象时,这个对象能够被垃圾回收器自动回收。因此它也不该该手动delete。对于在pkg文件中声明的构造函数,在lua中这个类都会有三个方法:new,new_local,.call回调与之对应。
固然咱们仅仅须要指定那些咱们须要导入到lua中的方法和成员。可是有时候咱们也须要声明一个没有任何方法和成员的类,这样作的目的是不打断继承链。
从tolua++ 1.0.5开始,可使用一个tolua_outside关键字指定一个正规函数做为一个类的方法或者静态方法,例:
/////////////// position.h:
typedef struct {
int x;
int y;
} Position;
Position* position_create();
void position_set(Position* pos, int x, int y);
/////////////// position.pkg:
struct Position {
int x;
int y;
static tolua_outside Position* position_create @ create();
tolua_outside void position_set @ set(int x, int y);
};
--------------- position.lua
local pos = Position:create()
pos:set(10, 10)
注意,在position_set方法中调用了一个position * 做为其第一个参数,这个参数在使用tolua_outside声明时将会被省略。
注意,咱们不能命名为new或者new_local方法,以及不能做为重载操做符,不然将会返回行为未定义。
Tolua自动绑定下面的二元操做符:
operator+ operator- operator* operator/
operator< operator>= operator== operator[]
对于这些关联操做符,tolua自动将返回的0值转换成nil,因此在C中的false将转变成lua中的false。
用上面的代码举一个例子,替换它的一个行为:
Point add (Point& other); // add points, returning another one
we had:
Point operator+ (Point& other); // add points, returning another one
这样在lua中咱们就能简写成:
p3 = p1 + p2
索引操做符(operator[])在只有一个数字参数时也能够导入到乱中。这种状况下,tolua支持返回一个引用或者一个基本类型。假如返回一个引用,在lua中,程序员便可以获取也能够设置它的值。假如返回一个非引用,程序员则只能获取它的值。举一个例子来讲明,假如咱们有一个vector的类,以及绑定了下面的操做符:
double& operator[] (int index);
在这种状况下,在lua中,咱们能够这样使用:
value = myVector[i] and myVector[i] = value
假如咱们按以下方式绑定操做符:
在lua中我就只能这样使用:
value = myVector[i]
释放函数(不是class成员)的函数重载不支持。
Tolua++扩展:从版本1.0.90开始支持转换操做符,例:
/////////////// node.h
// a class that holds a value that can be of type int, double, string or Object*
class Node { // tolua_export
private:
union {
int int_value;
double double_value;
string string_value;
Object* object_value;
};
// tolua_begin
public:
enum Type {
T_INT,
T_DOUBLE,
T_STRING,
T_OBJECT,
T_MAX,
};
Type get_type();
operator int();
operator double();
operator string();
operator Object*();
};
// tolua_end
Tolua++将会生成转换对象Node调用操做符的代码(使用C++ static_cast),并把在类中以“.typename”形式注册他们。例:
-- node.lua
local node = list.get_node("some_node") -- returns a Node object
if node.get_type() == Node.T_STRING then
print("node is "..node[".string"]())
elseif node.get_type() == Node.T_OBJECT then
local object = node[".Object*"]()
object:method()
end
Tolua++从1.0.6版本开始支持声明类属性,使用关键字tolua_property, 一个看起来像域的属性,可是其值是同调用类方法获取的。例:
/////////////// label.h
class Label {
public:
string get_name();
void set_name(string p_name);
Widget* get_parent();
};
/////////////// label.pkg
class Label {
tolua_property string name;
tolua_readonly tolua_property Widget* parent;
};
--------------- label.lua
local label = Label()
label.name = "hello"
print(label.name)
label.parent:show()
一个属性能够有不一样的类型,这些类型决定了如何设置和获取它的值。Tolua++提供了3中不一样的嵌入类型。
Default类型,默认状况下,访问用名字访问这个属性时将会调用其get_name 和set_name方法。
qt 类型将会使用name和setNmae方法。
overload 类型,将会都用name方法,只不过这个方法被重载了两个函数,获取函数为:'string name(void); 设置函数为:'void name(string);
这个属性的类型能够加在tolua_property关键字的后面,例:
tolua_property__qt string name;
当没有类型指定时,默认使用default类型,可是这是能够被改变的(见下文)
这默认的属性类型可使用宏'TOLUA_PROPERTY_TYPE'改变,这个宏将会在其调用为止改变属性类型,直到遇到宏所在位置的块结束符。例:
TOLUA_PROPERTY_TYPE(default); // default type for the 'global' scope
namespace GUI {
class Point {
tolua_property int x; // will use get_x/set_x
tolua_property int y; // will use get_y/set_y
};
TOLUA_PROPERTY_TYPE(qt); // changes default type to 'qt' for the rest of the 'GUI' namespace
class Label {
tolua_property string name; // will use name/setName
};
};
class Sprite {
tolua_property GUI::Point position; // will use get_position/set_position
tolua_property__overload string name; // will use name/name
};
自定义属性类型能够经过重定义函数get_property_methods_hook(细节见自定义tolua++章节)来增长。这个函数接受属性类型和名字,而且返回setter 和 getter函数名字。例:
/////////////// custom.lua
function get_property_methods_hook(ptype, name)
if ptype == "hungarian_string" then
return "sGet"..name, "Set"..name
end
if ptype == "hungarian_int" then
return "iGet"..name, "Set"..name
end
-- etc
end
/////////////// label.pkg
class Label {
tolua_property__hungarian_string string Name; // uses 'sGetName' and 'SetName'
tolua_property__hungarian_int string Type; // uses 'iGetType' and 'SetType'
};
Tolua++新增功能中支持模板类,使用指令TOLUA_TEMPLATE_BIND,例:
class vector {
TOLUA_TEMPLATE_BIND(T, int, string, Vector3D, double)
void clear();
int size() const;
const T& operator[](int index) const;
T& operator[](int index);
void push_back(T val);
vector();
~vector();
};
这个TOLUA_TEMPLATE_BIND指令,必须在类声明的一开始就使用,不然它将会被忽略。上面代码将会建立4个vector类。每个在TOLUA_TEMPLATE_BIND参数中指定的类型都将会体会宏T(在TOLUA_TEMPLATE_BIND中的第一个参数)。所以,函数operator[], &operator[] 和 push_back 将会有不一样的标识在不一样的对象版本中。在C++中一个对象将会使用vector<type> 声明,在lua中对应表名是vector_type_。因此lua中的代码能够这样写:
string_vector = vector_string_:new_local()
string_vector:push_back("hello")
string_vector:push_back("world")
print(string_vector[0].." "..string_vector[1])
一样的,一个模板类可能有多个宏,同时它也多是从其余模板类继承而来,例:
class hash_map : public map<K,V> {
TOLUA_TEMPLATE_BIND(K V, int string, string vector<double>)
V get_element(K key);
void set_element(K key, V value);
hash_map();
~hash_map();
};
在这个例子中,一个对象有其余模板做为其类型,因此它的声明为:hash_map<string,vector<double>>, 然而它在lua中对应的表为:hash_map_string_vector_double___(在类型重命名章节中有一个更好的方法访问这些对象)。
注意,因为定义某些模板类的复杂性,你应该关心如何声明它们。例如,你建立了类型(hash_map<string,vector<double> >)建立了一个对象,而后适用这个类型(hash_map<string, vector<double> >)声明一个变量(注意这个类型在string和vector之间多了一个空格),这个变量的类型没法认证的,无效的。比较好的方法是声明一个typedef,而且每个类型都适用它(这是C语言的经常使用作法)。咱们用上面的vector举个例子:
typedef vector VectorInt;
VectorInt variable;