找回密码
 立即注册
查看: 275|回复: 0

tolua++参考手册(翻译)

[复制链接]
发表于 2021-8-15 07:16 | 显示全部楼层 |阅读模式
原文地址: http://www.codenix.com/~tolua/tolua++.html
     译者注:在网上貌似没有对应的tolua中文版文档,故心血来潮,翻译了第一部分:tolua使用。第一次翻译,出了很多错误。可能还潜在很多的错误,建议和原版一起阅读。如果有错误,希望各位看官指出,谢谢。




Tolua++是tolua的升级版,是把C/C++和lua代码结合使用的一种工具。tolua++包括一些来自C++的新功能,如:
    支持std::string,并将其作为一种基本类型支持class template
当然,还有一些新的特点和bug的修复。
Tolua的使用大大简化了C/C++和lua的代码一体化。基于干净的头文件(或者扩展的头文件),tolua会自动生成相关的代码供lua访问C/C++使用。
使用Lua的API和标记方法(tag method facilities),tolua可以把C/C++里的常数,变量,函数,类和方法映射到lua。
这本手册是tolua++ 1.0版本,针对Lua5.0以上,基于tolua5.0。如果使用旧版本,请查看兼容性的详细资料。
       以下各节描述如何使用tolua 。如果你发现错误,或者有建议和意见,请与我们联系。
    How tolua worksHow to use toluaBasic ConceptsBinding constantsBinding external variablesBinding functionsBinding struct fieldsBinding classes and methodsBinding propertiesClass TemplatesModule definitionRenaming constants, variables and functionsStoring additional fieldsAdditional featuresExported utility functionsEmbedded Lua codeCustomizing tolua++Compatibility with older versionsChanges since v3 *Changes since v2.*Changes since v1.*CreditsAvailability
How tolua works

要使用tolua,我们需要创建package文件,C/C++干净的头文件(cleaned header file),只列出所有我们希望暴露给lua使用的常数,变量,函数,类和方法。然后tolua剖析这个文件,并自动创建C / C + +文件,该文件会自动把C/C++的代码绑定到Lua。当我们的程序链接到这个文件时,我们就能从Lua访问对应的C/C++代码。
Package文件可以包括常规头文件,其他Package文件,以及lua文件。
让我们先以一些例子。如果我们指定下列C头文件输入到tolua:

        #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);
这段代码绑定到Lua,会自动生成一个C文件。因此,在Lua代码里,我们能够访问对应的C代码,例如(writing, for instance:):
...myLine = createObject(LINE)...if isSelected(myLine) == TRUE then         rawObject(myLine, 1.0, 0.0, 0.0);else         rawObject(myLine, 1.0, 1.0, 1.0);end...
另外,考虑一个C++头文件:

        #define FALSE 0#define TRUE 1class 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++文件会自动生成,为Lua提供访问对应代码。因此,以下的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()        ...
Package文件传给tolua的不是真正的C/C++头文件,而是一种干净的版本(cleaned version)。Tolua并不执行一个完整的剖析解释的C/C++代码,只是解析一些声明,用来描述暴露给lua功能的声明(it understands a few declarations that are used to describe the features that are to be exported to Lua.)。
通常头文件可以包括进packages文件里,tolua会提取对应的代码用于解析对应的头文件(see Basic Concepts).。
How to use toLua

Tolua由两部分代码组成:可执行程序和静态库(an executable and a library)。可执行程序用于解析,从package文件读入,然后输出C/C++代码,该代码为lua提供访问C/C++的方法。如果package文件是与C++类似的代码(例如包括类的定义),就会生成一份C++代码。如果package文件是与C类似的代码(例如不包括类),就会生成一份C代码。Tolua接收一系列选择(tolua accepts a set of options)。运行“tolua -h”显示当前可接收的选择。例如,要解析一个名为myfile.pkg生成一个名为myfile.c的捆绑代码,我们需要输入:

tolua -o myfile.c myfile.pkg

产生的代码必须被应用程序生成和链接,才能提供给Lua进行访问。每个被解析的文件会生成一个package暴露给Lua。默认情况下,软件包的名称是根输入文件名称( myfile的例子),用户可以指定一个不同的名称给package:

tolua -n pkgname -o myfile.c myfile.pkg

package还应当明确初始化。我们需要声明和调用初始化函数,从化我们的C/C++代码初始化package。初始化函数被定义为:

int tolua_pkgname_open (lua_State*);
其中pkgname是被绑定package的名字。如果我们使用的是C++,我们可以选择自动初始化:

tolua -a -n pkgname -o myfile.c myfile.pkg

在这种情况下,初始化函数会自动调用。然而,如果我们计划使用多个Lua,自动初始化就行不通了,因为静态变量初始化的顺序在C++里没有定义。
Optionally, the prototype of the open function can be outputted to a header file, which name is given by the -H option.
Tolua生成的绑定代码使用了一系列tolua库里面的函数。因此,这个库同样需要被链接到应用程序中。同样,tolua.h需要加入编译生成代码。应用程序无需绑定任何package文件也可以使用tolua的面向对象框架(see exported utility functions)。在这种情况下,应用程序必须调用tolua初始化函数(此函数被称为任何package文件初始化功能) :

int tolua_open (void);

Basic Concepts

第一步是使用tolua创建package文件。从真正的头文件开始,我们重新将想要暴露给lua的特性转换成tolua可以理解的格式。
Including files

一个package文件可以包括其他的package文件。一般的格式是:
$pfile "include_file"
一个package文件同样可以包括一般的C/C++头文件,使用hfile或cfile指令:
$cfile "example.h"
在这种情况下, tolua将提取的代码封闭之间tolua_begin和tolua_end ,或tolua_export的一条直线上。以这个C++头为例思考:(注:这里并不是每个头文件都需要使用这些注释来告诉tolua要加入这些代码,仅仅是对于package文件包括的头文件而言)
#ifndef EXAMPLE_H#define EXAMPLE_Hclass Example { // tolua_exportprivate:        string name;        int number;public:        void set_number(int number);        //tolua_begin        string get_name();        int get_number();};// tolua_end#endif
In this case, the code that's not supported by tolua (the private part of the class), along with the functionset_number is left outside of the package that includes this header.
最后,lua文件可以被包括在package文件里,使用$lfile:
$lfile "example.lua"
新的tolua++:自1.0.4版本以来的tolua++,提供了一个额外的方式可以包括源文件,使用ifile:
$ifile "filename"
ifile还额外的可选参数后的文件名(ifile also takes extra optional parameters after the filename),例如:
$ifile "widget.h", GUI
$ifile "vector.h", math, 3d


ifile的默认行为是包括整个文件的原样。但是,该文件的内容和额外的参数在被纳入package之前通过include_file_hook函数进行处理 (see Customizing tolua++ for more details)。




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 headerAs 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的一般格式是:
       #defineNAME [ 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.)。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 12:10 , Processed in 0.069973 second(s), 22 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表