当前位置:文档之家› 分享LUA学习笔记(有用)

分享LUA学习笔记(有用)

LUA学习笔记一

首先提一下很容易找到的、比较正规的中文参考资料《PIL5.0中文教程》,《LUA使用手册5.1》(风云翻译)。

本节的目的在于在VS2005中搭建一个用于测试LUA的环境。侧重点为C/C++与LUA的交互--在CPP 文件中使用LUA脚本。

怎么编写.LUA文件:

任何文本编辑器都可以,只要写的语句符合LUA语法。保存的时候后缀名为“.LUA”。我用的LUA编辑器是LUAEdit,不过感觉除了能检测语法是不是正确之外,跟记事本没什么区别...

怎么配置LUA使用环境:

怎么生成编译后的LUA文件:

请参见https://www.doczj.com/doc/d05143939.html,/kun1234567/archive/2007/12/11/1929815.aspx

一定要解决上面3个问题,千万别急躁,直到你成功的配置了使用环境、能够写简单的.LUA文件并生成相应的二进制中间文件之前,不要继续阅读以下文字。

好了,让我们开始RTFS(Read The Fuxking Source)。

-------以下是LUA脚本--------

--test.LUA

function f ( x, y)

return x + y

end

---------通过LUAEdit语法测试--------------

//------------以下是test.c文件----------------

//==============================================

// LUA Test Object

// C++ Source LUA_test.cpp

//==============================================

//==============================================

// Include Files

//==============================================

extern "C"

{

#include "D:\\My Documents\\Visual Studio 2005\\Projects\\LUA\\LUA\\LUA.h"

#include "D:\\My Documents\\Visual Studio 2005\\Projects\\LUA\\LUA\\LUAlib.h"

#include "D:\\My Documents\\Visual Studio 2005\\Projects\\LUA\\LUA\\lauxlib.h"

}

//=============================================

// Libraries

//=============================================

#pragma comment( lib ,"D:\\My Documents\\Visual Studio 2005\\Projects\\LUA\\release\\LUA.lib") //=============================================

// Global Variables

//=============================================

LUA_State *L;

//=============================================

// LUA Functions

//=============================================

double f( double x, double y )

{

double ret;

LUA_getglobal( L, "f"); // 获取全局变量f

LUA_pushnumber( L,x); // 操作数压栈

LUA_pushnumber( L,y); // 操作数压栈

LUA_call( L, 2, 1); // 执行:2个操作数,1个返回值

//LUA_pcall( L, 2, 1, 0); // 保护模式的LUA_call,0为错误处理码。具体使用暂时不明,在使用手册中有粗略介绍

ret = LUA_tonumber( L, -1); // 将栈顶元素转换成数字并赋值给ret

LUA_pop( L, 1); // 从栈中弹出一个元素

return ret;

}

//==============================================

// Main Functions

//==============================================

int main( void)

{

int error;

L = LUA_open(); // 创建LUA接口指针(借用DX的术语,本质是个堆栈指针)

LUAopen_base(L); // 加载LUA基本库

LUAL_openlibs(L); // 加载LUA通用扩展库

/*

可能有的文章会采用以下写法,手工控制加载哪些库:

LUAopen_table(L); // 加载table库

LUAopen_io(L); // 加载IO库

LUAopen_string(L); // 加载string库

LUAopen_math(L); // 加载math库

经过测试,LUAopen_io(L);该句执行异常,可能跟LUA的IO库有关系。具体原因暂时没有追究,将来如果有机会弄清楚,再回头来阐述。

*/

/* load the script */

error = LUAL_loadfile(L, "test.LUA"); // 读取LUA源文件到内存

double ret = f( 10, 3.4); // 调用模版函数f

printf( "ret = %f", ret); // 输出结果,C语言的东西,跟LUA无关

getchar(); // console程序调试技巧,方便观察结果

LUA_close( L);// 关闭LUA接口

return 1;

}

你可以直接复制这些代码到.LUA 和.cpp文件里,也可以手动敲进去,我建议后者--就跟上课记笔记一样--好记性不如烂笔头。

编译成功了吗?如果不成功一定是环境没有配置好。不过就算编译成功也是白搭:D。程序在执行到

函数f()中的“ LUA_call( L, 2, 1);”这句时肯定会跳出来。如果你眼睛反映够快,并且英语够好,那么在0.3秒之内你将看到来自LUA的Debug信息:PANIC: unprotected error in call to LUA API (attempt to call a nil value),这是我在程序里故意留的一个BUG。

让我们来看看为什么出错了。

在调用“ LUA_call( L, 2, 1);”的时候“调用了一个空的值”,说明栈是空的。LUA在执行脚本中的函数的时候,首先会把函数体压栈,然后是操作数。

那么函数体去哪了?原因在于main()中的error = LUAL_loadfile(L, "test.LUA"); 这句。

首先LUA是“动态编译的脚本语言”,而loadfile只是把源文件加载到内存中,还少了“编译”这一步,可以用“LUAL_dofile(L,"test.LUA");”来替换,它既加载又编译。替换之后执行应该就没有问题了。

但是还没完,LUAL_dofile 实际上是个宏:

#define LUAL_dofile(L, fn) \

(LUAL_loadfile(L, fn) || LUA_pcall(L, 0, LUA_MULTRET, 0))

LUA_MULTRET也是宏定义,值为-1,表示函数有多个返回值(LUA规则,pil 24.2--堆栈)。

扩展开来就是以下两句:

LUAL_loadfile(L, fn);

LUA_pcall(L, 0, LUA_MULTRET, 0);

pcall以上述参数执行的时候,会把加载到内存中的源程序编译成可以用于执行的2进制代码,并将全局变量压栈(在LUA中,函数也是变量,pil 2.5 -- Functions,毕竟函数名和函数体是不同的2个东西)。就跟PE文件格式里的Section一样(PE文件就是Windows3.1之后的.exe/.dll文件)。当然如果你不知道什么PE文件也没关系--我只是打个比方--就当成VS2005编译代码时生成的.obj文件。

虽然实际使用中99%的情况都是直接使用dofile,但是我想将该问题提出来说可以更加直观的理解“动态编译”。

另外一个需要特别注意的是,在test.LUA中,定义了一个模板函数f,接受2个操作数( x , y ),所以需要在CPP文件中定义该函数的CPP模板double f ( double x, double y),它们一定是成对出现的(不然

我们为什么要用LUA呢:D)。当然,你也可以让CPP的函数 f 接受3个参数x, y, z,但是只把y , z 压栈,x单独做处理,同样的道理也可以使用于LUA文件。源代码我就不写了,你可以当作练习。

最后,我想谈谈LUAc.exe 和.OUT文件--也就是独立的LUA编译器生成的2进制文件。因为.LUA太直接,源代码可以直接看到,同时动态编译也是要花费额外的时间(可能还有别的理由,通过进一步的学习应该可以发现),所以使用.out文件是比较好的方案。

那么现在直接loadfile总可以了吧?很可惜不行!loadfile这个函数实在太傻了,只管读入文件,而负责判断读入的是源文件还是经过编译的二进制代码的工作是由LUA_pcall()来做的--如果是源文件,就编译后压栈,如果是二进制代码,就直接压栈。

再一个就是,如果你在程序中使用OUT文件,在VS2005中调试的时候,依然会报attempt to call a nil value的错误--就算你用的是LUAL_dofile()!

比如:error = LUAL_dofile(L, "LUAc.out");

LUA报错!不过直接执行生成的.exe文件却没有问题。这个问题的原因我还不清楚。

LUA学习笔记二--在LUA中使用自己的C函数

2011-11-01 20:21本节的目的:在LUA脚本中调用自己写的函数。侧重点为自己编写可以被LUA脚本使用的C函数的规则。

--------------------------------------------------------------------------------

啥都不说了,RTFS

-------以下是LUA脚本--------

--test.LUA

LUAC_MessageBox( "Last is ShowMessage! This is real MessageBox!");

---------通过LUAEdit语法测试才出鬼了--------------

//------------以下是test.cpp文件----------------

//================================================================================= // LUA Test Object

// C++ Source LUA_test.cpp

//================================================================================= //================================================================================= // Include Files

//================================================================================= extern "C"

{

#include "D:\\My Documents\\V isual Studio 2005\\Projects\\LUA\\LUA\\LUA.h"

#include "D:\\My Documents\\V isual Studio 2005\\Projects\\LUA\\LUA\\LUAlib.h"

#include "D:\\My Documents\\V isual Studio 2005\\Projects\\LUA\\LUA\\lauxlib.h"

}

#include

#include

#include

using namespace std;

//================================================================================= // Libraries

//================================================================================= #pragma comment( lib ,"D:\\My Documents\\V isual Studio 2005\\Projects\\LUA\\release\\LUA.lib")

//================================================================================= // Global V ariables

//================================================================================= LUA_State *L;

//================================================================================= // LUA Functions

//================================================================================= double f( double x, double y )

{

double ret;

LUA_getglobal( L, "f");

LUA_pushnumber( L,x);

LUA_pushnumber( L,y);

LUA_call( L, 2, 1);

//LUA_pcall( L, 2, 1, 0);

ret = LUA_tonumber( L, -1);

//LUA_pop( L, 1);

return ret;

}

//================================================================================= // C/C++ Functions

//================================================================================= int LUAC_MessageBox( LUA_State *L)

{

char Message[256] = "";

int i;

// 获取参数个数

int n = LUA_gettop(L);

// 保存全部参数

for ( i = 1, j = 0; i <= n ; i++)

{

if( LUA_isstring( L, i))

strcpy( Message, LUA_tostring( L, i));

}

// 执行逻辑

MessageBox( NULL, Message, "LUA Test", MB_OK );

// 返回值压栈

// 返回压栈参数的个数

return 0;

}

//================================================================================= // Main Functions

//================================================================================= int main( void)

{

int error;

L = LUA_open();

LUAopen_base(L);

LUAL_openlibs(L);

// 注册C/C++函数

LUA_register( L, "LUAC_MessageBox", LUAC_MessageBox);

// load the script

// 加入了错误处理

if ( (error = LUAL_dofile(L, "test.LUA")) != 0)

{

MessageBox( NULL, "出错啦:执行脚本出错!", "LUA Test", MB_OK );

return 0;

}

getchar();

LUA_close( L);

return 1;

}

--------------------------------------------------------------------------------

额....这就是系列文章的好处:即使CSDN把原来的弄丢了,我也可以根据上下文来重写丢失的文章。

如你所见,我将关键点全部标记为粗体。并且给出了足够的注释。让我们来一条条梳理:

1、LUAC_MessageBox( "Last is ShowMessage! This is real MessageBox!");

这就是LUA脚本调用自己写的函数。让我们接着看怎么实现这个函数的。

2、int LUAC_MessageBox( LUA_State *L)

这个就是函数的实体了,要想使自己的函数被LUA脚本所用,必须遵守以下规则:

使用int 函数名( LUA_State *L) 的形式来声明和定义函数。

使用LUA的堆栈来进行参数交换。

调用LUA的注册函数。

3、关于第2个规则,LUA是按照__fastcall 的规则传递参数的,也就是从左到右一个个传。

LUA压栈的顺序是:首先将函数压栈,然后将参数压栈。执行完毕后,将参数和函数都出栈,然后将返回值按顺序压栈。当然,还有很多类似的参数交换规则,不过别急我们慢慢来。

4、retrun 0;

在C函数中的返回值指的是返回值的数量。因为LUA脚本是支持多个返回值的,结合上条和这条规则来实现。

5、C文件中的函数体编写完了,还需要将该函数进行注册,如:

LUA_register( L, "LUAC_MessageBox", LUAC_MessageBox);

第二个参数为LUA脚本中的函数名,第三个为C文件中的函数名。

6、还有什么是我没涉及到的?返回值压栈!但是我想暂时不谈这个问题,因为我将(已经)在笔记四中详细讨论。所以,先熟悉这个基本流程。

7、OK。重写的好处在于:我可以直接把笔记四的东西搬过来。哈哈,又一次时间旅行。

LUA中有很多把各类返回值压栈的函数,只要你在LUA参考手册中搜索“LUA_push”,你可以得到如下结果:

LUA_pushboolean

LUA_pushinteger

LUA_pushfstring

LUA_pushlstring

LUA_pushnil

LUA_pushnumber

.....

有很多这样的函数,解释可以参看参考手册(一定要看!)。

但是在这个例子中,我们没有返回值,所以retrun 0。

LUA学习笔记三2011-11-01 20:22

通过上一节的知识,我们可以实现很多使用了,但是在GUI领域,实在有太多的变量--x,y,z,w,h,bitmap,algi,d1,d2,d3,dp1,dp2,dp3,whatever...,而LUA的栈默认元素上限为20个,所以必须掌握LUA特有的数据类型--表的使用。

本节的目的:在上节的基础上,加入表的使用。侧重点为用表来作为函数参数。

--------------------------------------------------------------------

请不要嫌我麻烦,这个系列文章是循序渐进式,而不是手册式,所以一定要掌握之前的知识,才可以继续阅读。

另外,请快速阅读pil的语法部分(1-7章),以便对LUA语法有大概的了解,这大概会花费1天的时间。

但是不要在for循环和迭代器上花费太多的时间--如果暂时不想过于深入的理解LUA,而只是看懂这些笔记的程度--那些东西会研究的,但不是现在,现在我们只是初心者而已。

OK,这是我最后一次罗嗦这个。

--------------------------------------------------------------------------------

又RTFS?!

-------以下是LUA脚本--------

--test.LUA

LUAC_MessageBoxEx{ hWnd = nil,

lpText = "Last is ShowMessage! This is real MessageBox!",

lpCaption = "LUA Test",

uType = 0} --0就是MB_OK

---------通过LUAEdit语法测试才出鬼了--------------

//------------以下是test.cpp文件----------------

//================================================================================= // LUA Test Object

// C++ Source LUA_test.cpp

//================================================================================= //================================================================================= // Include Files

//================================================================================= extern "C"

{

#include "D:\\My Documents\\V isual Studio 2005\\Projects\\LUA\\LUA\\LUA.h"

#include "D:\\My Documents\\V isual Studio 2005\\Projects\\LUA\\LUA\\LUAlib.h"

#include "D:\\My Documents\\V isual Studio 2005\\Projects\\LUA\\LUA\\lauxlib.h"

}

#include

#include

#include

using namespace std;

//================================================================================= // Libraries

//================================================================================= #pragma comment( lib ,"D:\\My Documents\\V isual Studio 2005\\Projects\\LUA\\release\\LUA.lib")

//================================================================================= // Global V ariables

//=================================================================================

LUA_State *L;

//================================================================================= // LUA Functions

//================================================================================= double f( double x, double y )

{

double ret;

LUA_getglobal( L, "f");

LUA_pushnumber( L,x);

LUA_pushnumber( L,y);

LUA_call( L, 2, 1);

//LUA_pcall( L, 2, 1, 0);

ret = LUA_tonumber( L, -1);

//LUA_pop( L, 1);

return ret;

}

//================================================================================= // C/C++ Helper Functions

//================================================================================= // 获取t[k](表t中字段k)的值

// 有以下的值

#define GTC_LP 0x0000 // pointer

#define GTC_INT 0x0001 // int

#define GTC_DOUBLE 0x0002 // double

#define GTC_STRING 0x0003 // char

void _LUA_getfield( LUA_State* L, char* key, void* ret, int type_flags)

{

LUA_pushstring( L, key);

LUA_gettable( L, -2);

switch( type_flags)

{

case GTC_LP:

{

ret = (void*)LUA_topointer(L,-1);

break;

}

case GTC_INT:

{

(*(int*)ret) = (int)LUA_tointeger( L, -1);

break;

}

case GTC_DOUBLE:

{

(*(double*)ret) = LUA_tonumber( L, -1);

break;

}

case GTC_STRING:

{

// 此处有隐患,应该阅读LUA_tostring()原型之后再来处理

strcpy( (char*)ret, LUA_tostring( L, -1));

break;

}

default:

break;

}

LUA_pop( L, 1);

}

//================================================================================= // C/C++ Functions

//================================================================================= int LUAC_MessageBox( LUA_State *L)

{

char Message[256] = "";

int i;

// 获取参数个数

int n = LUA_gettop(L);

// 保存全部参数

for ( i = 1, j = 0; i <= n ; i++)

{

if( LUA_isstring( L, i))

strcpy( Message, LUA_tostring( L, i));

}

// 执行逻辑

MessageBox( NULL, Message, "LUA Test", MB_OK );

// 返回值压栈

// 返回压栈参数的个数

return 0;

}

int LUAC_MessageBoxEx( LUA_State *L)

{

HWND hWnd;

char lpText[256] = "";

char lpCaption[256] = "";

UINT uType;

// 解析表

_LUA_getfield( L, "hWnd", &hWnd, GTC_LP);

_LUA_getfield( L, "lpText", &lpText, GTC_STRING);

_LUA_getfield( L, "lpCaption", &lpCaption, GTC_STRING);

_LUA_getfield( L, "uType", &uType, GTC_INT);

// 执行逻辑

MessageBox( hWnd, lpText, lpCaption, uType);

// 返回值压栈

// 返回压栈参数的个数

return 0;

}

//================================================================================= // Main Functions

//================================================================================= int main( void)

{

int error;

L = LUA_open();

LUAopen_base(L);

LUAL_openlibs(L);

// 注册C/C++函数

LUA_register( L, "LUAC_MessageBox", LUAC_MessageBox);

LUA_register( L, "LUAC_MessageBoxEx", LUAC_MessageBoxEx);

// load the script

// 加入了错误处理

if ( (error = LUAL_dofile(L, "test.LUA")) != 0)

{

MessageBox( NULL, "出错啦:执行脚本出错!", "LUA Test", MB_OK );

return 0;

}

getchar();

LUA_close( L);

return 1;

}

--------------------------------------------------------------------------------

如你所见,这2段代码是相当的熟悉,因为我直接使用上次的代码来做的修改。并且我将新添加的部分用黑体标明,以引起你的注意。所有这写新加入的代码,相应的注释里有足够的解释--除了一个帮助器函数:_LUA_getfield

这一节新加的代码比较少,但是要说清楚其中的内容就不太容易了。一定要关注我用红色标记出来的部分。

1、首先注意LUA文件中的{},这是表语法。LUA语法规定,如果(LUA文件中的)函数的参数只有1个参数,并且该参数类型为一张表,就可以使用形如: 函数名{ }的形式。

2、其次是怎么解析一张表。在LUA规则里,这样定义读取表元素的协议的:

LUA API 只提供了一个LUA_gettable 函数,他接受table 在栈中的位置为参数,将对应key 值出栈,返回与key 对应的value 。我们上面的getfield 函数假定table 在栈顶,因此,LUA_pushstring 将key 入栈之后,table 在-2 的位置。返回之前,getfield 会将栈恢复到调用前的状态。(取自PIL 25.1--表操作)

这段话有点含糊不清,实际上LUA取元素的协议是:首先将一个字符串压栈,然后调用LUA_gettable。LUA_gettable的实际的工作流程是:以栈顶的字符串(key)为关键字,在栈索引位置( -2)的表(table)中查询该关键字的值(value),然后将栈顶的key出栈,再将value压栈。

3、最后一个LUA_pop( L, 1)的作用就是将这个返回值出栈,以保持栈的原貌。

好好理解上面被标记为粗体的文字,因为这就是表的基本使用的全部--只是基本使用。

最后,我想谈谈为什么不能通过LUAEdit的语法检测。原因很简单:LUAEdit所使用的LUA库中不包含我们写的这些个函数的声明---这就是为什么我说LUAEdit跟记事本没什么区别,事实上我除了学习LUA语法的阶段使用了它,现在一直都是用记事本在写脚本。当然,在研究LUA的同时,我也会尝试研究下LUAEdit 的使用--因为它长的跟VS2005很象,呃...

LUA学习笔记四(1)

2011-11-01 20:24

通过上一节的知识,我们学会了怎么利用表来作为参数,这样我们能实现的功能更多了。但是有一点是没有涉及到的,那就是返回值,而返回值的讨论不能不提userdata类型。

本节的目的:讨论怎么处理LUA返回值和Userdata类型的使用。

本章涉及一些Allegro的知识,但是这不是重点。所以当你看到不熟悉的代码段时,不必太在意,把注意力放在编写LUA函数的套路上。

--------------------------------------------------------------------------------

现在我可以在脚本中写下“CreateWindow{x=0,y=0,w=1024,h=768} ”这样的语句来控制程序在内存中创建一个窗口了,很美妙是不是?

但是我现在要实现“AddButton{x=100,y=100,w=75,h=25}”这样添加控件,应该怎么做呢?

首先我得让AddButton接受一个类似窗口句柄的参数,那样AddButton才知道往哪个窗口里添加一个新按钮。

因此CreateWindow就得返回这个窗口句柄。

OK。新的LUA语句象这个样子:

NewWindow = CreateWindow{ x = 0,y = 0, w = 1024, h = 768} ;

AddButton{hWindow = NewWindow , x =100, y = 100, w = 75, h = 25};

看起来真好,现在让我们来实现它。

--------------------------------------------------------------------------------

RRRRRRRTFS!

-------以下是LUA脚本--------

--test.LUA

NewWindow = CreateWindow{ x = 0, y = 0, w = 1024, h = 768} ;

AddButton{hWindow = NewWindow , x =100, y = 100, w = 75, h = 25};

---------绝对不会通过LUAEdit语法测试--------------

//------------以下是test.cpp文件----------------

// 由于总是在上一次的基础上进行的添加,因此已经实现过了的函数体我就省略了。

// 这也是与“给出完整可编译代码段”的原则的妥协---不然代码就太多了。

// 另外这次的代码是没办法直接通过编译的,因为使用到了Allegro库和一套自己编写的控件。

// 但是这些代码跟包含Allegro相关的代码没有区别--注意怎么使用LUA的套路就好。

//================================================================================= // LUA Test Object

// C++ Source LUA_test.cpp

//=================================================================================

//================================================================================= // Include Files

//================================================================================= extern "C"

{

#include "D:\\My Documents\\V isual Studio 2005\\Projects\\LUA\\LUA\\LUA.h"

#include "D:\\My Documents\\V isual Studio 2005\\Projects\\LUA\\LUA\\LUAlib.h"

#include "D:\\My Documents\\V isual Studio 2005\\Projects\\LUA\\LUA\\lauxlib.h"

}

// 这是一套控件,利用了Allegro库,不必在意。

#include "d:\My Documents\V isual Studio 2005\Projects\Demo\Demo\Components.h"

#include

#include

#include

using namespace std;

//================================================================================= // Libraries

//================================================================================= #pragma comment( lib ,"D:\\My Documents\\V isual Studio 2005\\Projects\\LUA\\release\\LUA.lib")

//================================================================================= // macro

//================================================================================= // 计算Dialog数组长度

#define DLG_COUNT(x) ( sizeof(x)/sizeof(DIALOG))

// 计算Dialog数组的大小

#define DLG_SIZEOF(x) ( sizeof( *x) + (x##->count -1) * sizeof( x##->lpdialog[0]))

// 在堆上初始化变量

#define NEW(x) ( (x##_PTR)malloc( sizeof(x)))

//================================================================================= // Global V ariables

//================================================================================= // 定义的结构体。因为在Allegro中,窗体是一个Dialog类型的数组。不必在意。

typedef struct DIALOG_ARRAY_TAG

{

int count;

DIALOG lpdialog[1];

}DIALOG_ARRAY, *DIALOG_ARRAY_PTR;

//================================================================================= // Global V ariables

//================================================================================= LUA_State *L;

//================================================================================= // LUA Functions

//================================================================================= double f( double x, double y )

{

}

//================================================================================= // C/C++ Helper Functions

//================================================================================= // 获取t[k](表t中字段k)的值

void _LUA_getfield( LUA_State* L, char* key, void* ret, int type_flags)

{

}

//================================================================================= // C/C++ Functions

//================================================================================= int LUAC_MessageBox( LUA_State *L)

{

}

int LUAC_MessageBoxEx( LUA_State *L)

{

}

// 创建一个新DIALOG数组

// 返回该数组指针

int CreateWindow( LUA_State* L)

{

// LUA V ar

int x,y,w,h;

void* lp;

// NewWindow = CreateWindow{ x = __in, y = __in, w = __in, h = __in}

// 从堆栈上获取参数

_LUA_getfield( L, "x", &x, GTC_INT);

_LUA_getfield( L, "y", &y, GTC_INT);

_LUA_getfield( L, "w", &w, GTC_INT);

_LUA_getfield( L, "h", &h, GTC_INT);

// allegro var 不必在意

DIALOG_ARRAY_PTR new_dialog = NEW(DIALOG_ARRAY);

new_dialog->count = 0;

DIALOG lpbasic_dialog[] = //一个基本的Allegro窗体数组模板,不必在意

{

/* (dialog proc)(x)(y)(w)(h)(fg)(bg)(key)(flags)(d1)(d2)(dp)(dp2)(dp3) */

{ d_clear_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL,NULL,NULL}, { d_yield_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL,NULL,NULL}, { NULL}

};

// 将窗体模板添加到新的数据结构DIALOG_ARRAY,不必在意

new_dialog = add_object( DLG_COUNT(lpbasic_dialog), new_dialog, lpbasic_dialog);

//使用获得的参数

new_dialog->lpdialog[0].x = x;

new_dialog->lpdialog[0].y = y;

new_dialog->lpdialog[0].w = w;

new_dialog->lpdialog[0].h = h;

// 返回值压栈

lp = LUA_newuserdata( L, DLG_SIZEOF(new_dialog));

// 拷贝数组内容

memcpy( lp, new_dialog, DLG_SIZEOF(new_dialog));

// 返回压栈参数的个数

return 1;

}

//================================================================================= // Main Functions

//================================================================================= int main( void)

{

int error;

L = LUA_open();

LUAopen_base(L);

LUAL_openlibs(L);

// 注册C/C++函数

LUA_register( L, "LUAC_MessageBox", LUAC_MessageBox);

LUA_register( L, "LUAC_MessageBoxEx", LUAC_MessageBoxEx);

LUA_register( L, "CreateWindow", CreateWindow);

// load the script

// 加入了错误处理

if ( (error = LUAL_dofile(L, "test.LUA")) != 0)

{

MessageBox( NULL, "出错啦:执行脚本出错!", "LUA Test", MB_OK );

return 0;

}

getchar();

LUA_close( L);

return 1;

}

LUA学习笔记四(2)

2011-11-01 20:24

恩,这次笔记需要注意的地方是不是醒目多了?

现在让我们来逐个分析:

返回值为UserData类型。

OK,我知道我违反了介绍的原则---先易后难,但是普通的返回值确实是很简单的事情,所以在我们讨论清楚userdata类型之后,再一句话带过普通类型的返回值。

int CreateWindow( LUA_State* L)

该函数中第1个有意思的地方在于结尾对返回值的压栈:

// 返回值压栈

lp = LUA_newuserdata( L, DLG_SIZEOF(new_dialog));

LUA中有很多把各类返回值压栈的函数,只要你在LUA参考手册中搜索“LUA_push”,你可以得到如下结果:

LUA_pushboolean

LUA_pushinteger

LUA_pushfstring

LUA_pushlstring

LUA_pushnil

LUA_pushnumber

.....

有很多这样的函数,解释可以参看参考手册。

我想谈的是他们的共性:这一系列的类型压栈都是已经分配了内存空间的类型。

所以你找不到函数LUA_pushuserdata,原因是Userdata实际是指一块内存空间,就象malloc出来的一样。不同的是,这段空间是在LUA用于交互的栈(LUAState* L)上进行分配的。所以你要将自己的数据类型压栈的话,就需要按照以下步骤来:

1、调用LUA_newuserdata()函数申请指定大小的userdata类型,该函数返回指向新内存的指针。如:

lp = LUA_newuserdata( L, DLG_SIZEOF(new_dialog));

2、改变lp指向的这段内存的内容--随便写些000111进去,或者改成你要的数据。但是有一点需要注意的是,不要尝试改变该块内存的大小,否则会对LUA的栈造成破坏。何况LUA也不会让你改变这块内存~

第2个有意思的地方是怎么利用刚刚申请的这段空间。因为在LUA的栈上,所以操作起来不如在堆上的数据那么自由,把堆上的数据往这个栈拷贝的时候会出现失败的情况---至少我用memmove函数进行操作时失败了。

// 拷贝数组内容

memcpy( lp, new_dialog, DLG_SIZEOF(new_dialog));

现在再来谈谈普通的返回值压栈:调用对应类型的函数!

最后,告诉LUA,你要返回的参数个数,在这里,我要返回刚才创建的窗体树组,所以是1个。

// 返回压栈参数的个数

return 1;

相关主题
文本预览
相关文档 最新文档