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;